Split libsoup out of soup. ChangeLog.old contains the original soup
[platform/upstream/libsoup.git] / libsoup / soup-message.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-message.c: Asyncronous Callback-based HTTP Request Queue.
4  *
5  * Authors:
6  *      Alex Graveley (alex@helixcode.com)
7  *
8  * Copyright (C) 2000, Helix Code, Inc.
9  */
10
11 #include "soup-auth.h"
12 #include "soup-error.h"
13 #include "soup-message.h"
14 #include "soup-misc.h"
15 #include "soup-context.h"
16 #include "soup-private.h"
17 #include "soup-queue.h"
18 #include "soup-transfer.h"
19
20 /**
21  * soup_message_new:
22  * @context: a %SoupContext for the destination endpoint.
23  * @method: a string which will be used as the HTTP method for the created
24  * request, if NULL a GET request will be made.
25  * 
26  * Creates a new empty %SoupMessage, which will connect to the URL represented
27  * by @context.  A reference will be added to @context.
28  * 
29  * The new message has a status of @SOUP_STATUS_IDLE.
30  *
31  * Return value: the new %SoupMessage.
32  */
33 SoupMessage *
34 soup_message_new (SoupContext *context, const gchar *method) 
35 {
36         SoupMessage *ret;
37
38         ret          = g_new0 (SoupMessage, 1);
39         ret->priv    = g_new0 (SoupMessagePrivate, 1);
40         ret->status  = SOUP_STATUS_IDLE;
41         ret->method  = method ? method : SOUP_METHOD_GET;
42
43         ret->request_headers = g_hash_table_new (soup_str_case_hash, 
44                                                  soup_str_case_equal);
45
46         ret->response_headers = g_hash_table_new (soup_str_case_hash, 
47                                                   soup_str_case_equal);
48
49         ret->priv->http_version = SOUP_HTTP_1_1;
50
51         soup_message_set_context (ret, context);
52
53         return ret;
54 }
55
56 /**
57  * soup_message_new_full:
58  * @context: a %SoupContext for the destination endpoint.
59  * @method: a string which will be used as the HTTP method for the created
60  * request, if NULL a GET request will be made..
61  * @req_owner: the %SoupOwnership of the passed data buffer.
62  * @req_body: a data buffer containing the body of the message request.
63  * @req_length: the byte length of @req_body.
64  * 
65  * Creates a new %SoupMessage, which will connect to the URL represented by
66  * @context.  A reference is added to @context.  The request data
67  * buffer will be filled from @req_owner, @req_body, and @req_length
68  * respectively.
69  *
70  * The new message has a status of @SOUP_STATUS_IDLE.
71  *
72  * Return value: the new %SoupMessage.
73  */
74 SoupMessage *
75 soup_message_new_full (SoupContext   *context,
76                        const gchar   *method,
77                        SoupOwnership  req_owner,
78                        gchar         *req_body,
79                        gulong         req_length)
80 {
81         SoupMessage *ret = soup_message_new (context, method);
82
83         ret->request.owner = req_owner;
84         ret->request.body = req_body;
85         ret->request.length = req_length;
86
87         return ret;
88 }
89
90 static void
91 copy_header (gchar *key, gchar *value, GHashTable *dest_hash)
92 {
93         soup_message_add_header (dest_hash, key, value);
94 }
95
96 /*
97  * Copy context, method, error, request buffer, response buffer, request
98  * headers, response headers, message flags, http version.  
99  *
100  * Callback function, content handlers, message status, server, server socket,
101  * and server message are NOT copied.  
102  */
103 SoupMessage *
104 soup_message_copy (SoupMessage *req)
105 {
106         SoupMessage *cpy;
107
108         cpy = soup_message_new (req->context, req->method);
109
110         cpy->errorcode = req->errorcode;
111         cpy->errorclass = req->errorclass;
112         cpy->errorphrase = req->errorphrase;
113
114         cpy->request.owner = SOUP_BUFFER_SYSTEM_OWNED;
115         cpy->request.length = cpy->request.length;
116         cpy->request.body = g_memdup (req->request.body, 
117                                       req->request.length);
118
119         cpy->response.owner = SOUP_BUFFER_SYSTEM_OWNED;
120         cpy->response.length = cpy->response.length;
121         cpy->response.body = g_memdup (req->response.body, 
122                                        req->response.length);
123
124         soup_message_foreach_header (req->request_headers,
125                                      (GHFunc) copy_header,
126                                      cpy->request_headers);
127
128         soup_message_foreach_header (req->response_headers,
129                                      (GHFunc) copy_header,
130                                      cpy->response_headers);
131
132         cpy->priv->msg_flags = req->priv->msg_flags;
133
134         /* 
135          * Note: We don't copy content handlers or callback function as this is
136          * a nightmare double free situation.  
137          */
138
139         cpy->priv->http_version = req->priv->http_version;
140
141         return cpy;
142 }
143
144 static void 
145 release_connection (const SoupDataBuffer *data,
146                     gpointer              user_data)
147 {
148         SoupConnection *conn = user_data;
149         soup_connection_release (conn);
150 }
151
152 static void 
153 release_and_close_connection (gboolean headers_done, gpointer user_data)
154 {
155         SoupConnection *conn = user_data;
156         soup_connection_set_keep_alive (conn, FALSE);
157         soup_connection_release (conn);
158 }
159
160 /**
161  * soup_message_cleanup:
162  * @req: a %SoupMessage.
163  * 
164  * Frees any temporary resources created in the processing of @req.  Also
165  * releases the active connection, if one exists. Request and response data
166  * buffers are left intact. 
167  */
168 void 
169 soup_message_cleanup (SoupMessage *req)
170 {
171         g_return_if_fail (req != NULL);
172
173         if (req->connection && 
174             req->priv->read_tag &&
175             req->status == SOUP_STATUS_READING_RESPONSE) {
176                 soup_transfer_read_set_callbacks (req->priv->read_tag,
177                                                   NULL,
178                                                   NULL,
179                                                   release_connection,
180                                                   release_and_close_connection,
181                                                   req->connection);
182                 req->priv->read_tag = 0;
183                 req->connection = NULL;
184                 /* 
185                  * The buffer doesn't belong to us until the message is 
186                  * finished.
187                  */
188                 req->response.owner = SOUP_BUFFER_STATIC;
189         }
190
191         if (req->priv->read_tag) {
192                 soup_transfer_read_cancel (req->priv->read_tag);
193                 req->priv->read_tag = 0;
194         }
195
196         if (req->priv->write_tag) {
197                 soup_transfer_write_cancel (req->priv->write_tag);
198                 req->priv->write_tag = 0;
199         }
200
201         if (req->priv->connect_tag) {
202                 soup_context_cancel_connect (req->priv->connect_tag);
203                 req->priv->connect_tag = NULL;
204         }
205
206         if (req->connection) {
207                 soup_connection_release (req->connection);
208                 req->connection = NULL;
209         }
210
211         soup_queue_remove_request (req);
212 }
213
214 static void
215 finalize_message (SoupMessage *req)
216 {
217         if (req->context)
218                 soup_context_unref (req->context);
219
220         if (req->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
221                 g_free (req->request.body);
222         if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
223                 g_free (req->response.body);
224
225         soup_message_clear_headers (req->request_headers);
226         g_hash_table_destroy (req->request_headers);
227
228         soup_message_clear_headers (req->response_headers);
229         g_hash_table_destroy (req->response_headers);
230
231         g_slist_foreach (req->priv->content_handlers, (GFunc) g_free, NULL);
232         g_slist_free (req->priv->content_handlers);
233
234         g_free ((gchar *) req->errorphrase);
235         g_free (req->priv);
236         g_free (req);
237 }
238
239 /**
240  * soup_message_free:
241  * @req: a %SoupMessage to destroy.
242  * 
243  * Destroys the %SoupMessage pointed to by @req. Request and response headers
244  * are freed. Request and response data buffers are also freed if their
245  * ownership is %SOUP_BUFFER_SYSTEM_OWNED. The message's destination context
246  * will be de-referenced.
247  */
248 void 
249 soup_message_free (SoupMessage *req)
250 {
251         g_return_if_fail (req != NULL);
252
253         soup_message_cleanup (req);
254
255         finalize_message (req);
256 }
257
258 /**
259  * soup_message_issue_callback:
260  * @req: a %SoupMessage currently being processed.
261  * @error: a %SoupErrorCode to be passed to %req's completion callback.
262  * 
263  * Finalizes the message request, by first freeing any temporary resources, then
264  * issuing the callback function pointer passed in %soup_message_new or
265  * %soup_message_new_full. If, after returning from the callback, the message
266  * has not been requeued, @msg is destroyed using %soup_message_free.
267  */
268 void
269 soup_message_issue_callback (SoupMessage *req)
270 {
271         g_return_if_fail (req != NULL);
272
273         /* 
274          * Make sure we don't have some icky recursion if the callback 
275          * runs the main loop, and the connection has some data or error 
276          * which causes the callback to be run again.
277          */
278         soup_message_cleanup (req);
279
280         if (req->priv->callback) {
281                 (*req->priv->callback) (req, req->priv->user_data);
282
283                 if (req->status != SOUP_STATUS_QUEUED)
284                         finalize_message (req);
285         }
286 }
287
288 /**
289  * soup_message_cancel:
290  * @req: a %SoupMessage currently being processed.
291  * 
292  * Cancel a running message, and issue completion callback with a
293  * %SoupTransferStatus of %SOUP_ERROR_CANCELLED. If not requeued by the
294  * completion callback, the @msg will be destroyed.
295  */
296 void 
297 soup_message_cancel (SoupMessage *msg) 
298 {
299         soup_message_set_error (msg, SOUP_ERROR_CANCELLED);
300
301         /* Kill the connection as a safety measure */
302         if (msg->connection)
303                 soup_connection_set_keep_alive (msg->connection, FALSE);
304
305         soup_message_issue_callback (msg);
306 }
307
308 static gboolean 
309 foreach_free_header_list (gchar *name, GSList *vals, gpointer notused)
310 {
311         g_free (name);
312         g_slist_foreach (vals, (GFunc) g_free, NULL);
313         g_slist_free (vals);
314
315         return TRUE;
316 }
317
318 void
319 soup_message_clear_headers       (GHashTable        *hash)
320 {
321         g_return_if_fail (hash != NULL);
322
323         g_hash_table_foreach_remove (hash, 
324                                      (GHRFunc) foreach_free_header_list, 
325                                      NULL);
326 }
327
328 void 
329 soup_message_remove_header (GHashTable  *hash,
330                             const gchar *name)
331 {
332         gchar *stored_key;
333         GSList *vals;
334
335         g_return_if_fail (hash != NULL);
336         g_return_if_fail (name != NULL || name [0] != '\0');
337
338         if (g_hash_table_lookup_extended (hash, 
339                                           name, 
340                                           (gpointer *) &stored_key, 
341                                           (gpointer *) &vals)) {
342                 g_hash_table_remove (hash, name);
343                 foreach_free_header_list (stored_key, vals, NULL);
344         }
345 }
346
347 void 
348 soup_message_add_header (GHashTable  *hash,
349                          const gchar *name,
350                          const gchar *value) 
351 {
352         GSList *old_value;
353
354         g_return_if_fail (hash != NULL);
355         g_return_if_fail (name != NULL || name [0] != '\0');
356         g_return_if_fail (value != NULL);
357
358         old_value = g_hash_table_lookup (hash, name);
359
360         if (old_value)
361                 g_slist_append (old_value, g_strdup (value));
362         else
363                 g_hash_table_insert (hash, 
364                                      g_strdup (name), 
365                                      g_slist_append (NULL, 
366                                                      g_strdup (value)));
367 }
368
369 /**
370  * soup_message_get_header:
371  * @req: a %SoupMessage.
372  * @name: header name.
373  * 
374  * Lookup the first transport header with a key equal to @name.
375  *
376  * Return value: the header's value or NULL if not found.
377  */
378 const gchar *
379 soup_message_get_header (GHashTable *hash,
380                          const gchar *name)
381 {
382         GSList *vals;
383
384         g_return_val_if_fail (hash != NULL, NULL);
385         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);  
386
387         vals = g_hash_table_lookup (hash, name);
388         if (vals) 
389                 return vals->data;
390
391         return NULL;
392 }
393
394 /**
395  * soup_message_get_header_list:
396  * @req: a %SoupMessage.
397  * @name: header name.
398  * 
399  * Lookup the all transport request headers with a key equal to @name.
400  *
401  * Return value: a const pointer to a GSList of header values or NULL if not
402  * found.  
403  */
404 const GSList *
405 soup_message_get_header_list (GHashTable  *hash,
406                               const gchar *name)
407 {
408         g_return_val_if_fail (hash != NULL, NULL);
409         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);  
410
411         return g_hash_table_lookup (hash, name);
412 }
413
414 typedef struct {
415         GHFunc   func;
416         gpointer user_data;
417 } ForeachData;
418
419 static void 
420 foreach_value_in_list (gchar *name, GSList *vals, ForeachData *data)
421 {
422         while (vals) {
423                 gchar *v = vals->data;
424
425                 (*data->func) (name, v, data->user_data);
426
427                 vals = vals->next;
428         }
429 }
430
431 void
432 soup_message_foreach_header      (GHashTable        *hash,
433                                   GHFunc             func,
434                                   gpointer           user_data)
435 {
436         ForeachData data = { func, user_data };
437
438         g_return_if_fail (hash != NULL);
439         g_return_if_fail (func != NULL);
440
441         g_hash_table_foreach (hash, (GHFunc) foreach_value_in_list, &data);
442 }
443
444 typedef struct {
445         GHRFunc   func;
446         gpointer user_data;
447 } ForeachRemoveData;
448
449 static gboolean 
450 foreach_remove_value_in_list (gchar             *name, 
451                               GSList            *vals, 
452                               ForeachRemoveData *data)
453 {
454         GSList *iter = vals;
455
456         while (iter) {
457                 gchar *v = iter->data;
458                 gboolean ret = FALSE;
459
460                 ret = (*data->func) (name, v, data->user_data);
461                 if (ret) {
462                         GSList *next = iter->next;
463
464                         vals = g_slist_remove (vals, v);
465                         g_free (v);
466
467                         iter = next;
468                 } else
469                         iter = iter->next;
470         }
471
472         if (!vals) {
473                 g_free (name);
474                 return TRUE;
475         } 
476
477         return FALSE;
478 }
479
480 void
481 soup_message_foreach_remove_header (GHashTable        *hash,
482                                     GHRFunc            func,
483                                     gpointer           user_data)
484 {
485         ForeachRemoveData data = { func, user_data };
486
487         g_return_if_fail (hash != NULL);
488         g_return_if_fail (func != NULL);
489
490         g_hash_table_foreach_remove (hash, 
491                                      (GHRFunc) foreach_remove_value_in_list, 
492                                      &data);
493 }
494
495 /**
496  * soup_message_set_request_header:
497  * @req: a %SoupMessage.
498  * @name: header name.
499  * @value: header value.
500  *
501  * ** DEPRECATED **
502  * 
503  * Adds a new transport header to be sent on an outgoing request. Passing a NULL
504  * @value will remove all headers with a name equal to @name.
505  */
506 void
507 soup_message_set_request_header (SoupMessage *req,
508                                  const gchar *name,
509                                  const gchar *value) 
510 {
511         g_return_if_fail (req != NULL);
512         g_return_if_fail (name != NULL || name [0] != '\0');
513
514         g_warning ("soup_message_set_request_header is DEPRECATED. Use "
515                    "soup_message_add_header, with msg->request_headers as "
516                    "the first argument.\n");
517
518         soup_message_add_header (req->request_headers, name, value);
519 }
520
521 /**
522  * soup_message_get_request_header:
523  * @req: a %SoupMessage.
524  * @name: header name.
525  * 
526  * ** DEPRECATED **
527  * 
528  * Lookup the first transport request header with a key equal to @name.
529  *
530  * Return value: the header's value or NULL if not found.
531  */
532 const gchar *
533 soup_message_get_request_header (SoupMessage *req,
534                                  const gchar *name) 
535 {
536         GSList *vals;
537         g_return_val_if_fail (req != NULL, NULL);
538         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
539
540         g_warning ("soup_message_get_request_header is DEPRECATED. Use "
541                    "soup_message_get_header, with msg->request_headers as "
542                    "the first argument.\n");
543
544         if (req->request_headers) {
545                 vals = g_hash_table_lookup (req->request_headers, name);
546                 if (vals) 
547                         return vals->data;
548         }
549
550         return NULL;
551 }
552
553 /**
554  * soup_message_set_response_header:
555  * @req: a %SoupMessage.
556  * @name: header name.
557  * @value: header value.
558  * 
559  * ** DEPRECATED **
560  * 
561  * Adds a new transport header to be sent on an outgoing response. Passing a
562  * NULL @value will remove all headers with a name equal to @name.
563  */
564 void
565 soup_message_set_response_header (SoupMessage *req,
566                                   const gchar *name,
567                                   const gchar *value) 
568 {
569         g_return_if_fail (req != NULL);
570         g_return_if_fail (name != NULL || name [0] != '\0');
571
572         g_warning ("soup_message_set_response_header is DEPRECATED. Use "
573                    "soup_message_add_header, with msg->response_headers as "
574                    "the first argument.\n");
575
576         soup_message_add_header (req->response_headers, name, value);
577 }
578
579 /**
580  * soup_message_get_response_header:
581  * @req: a %SoupMessage.
582  * @name: header name.
583  * 
584  * ** DEPRECATED **
585  * 
586  * Lookup the transport response header with a key equal to @name.
587  *
588  * Return value: the header's value or NULL if not found.
589  */
590 const gchar *
591 soup_message_get_response_header (SoupMessage *req,
592                                   const gchar *name) 
593 {
594         GSList *vals;
595         g_return_val_if_fail (req != NULL, NULL);
596         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
597
598         g_warning ("soup_message_get_response_header is DEPRECATED. Use "
599                    "soup_message_get_header, with msg->response_headers as "
600                    "the first argument.\n");
601
602         if (req->response_headers) {
603                 vals = g_hash_table_lookup (req->response_headers, name);
604                 if (vals) 
605                         return vals->data;
606         }
607
608         return NULL;
609 }
610
611 /**
612  * soup_message_queue:
613  * @req: a %SoupMessage.
614  * @callback: a %SoupCallbackFn which will be called after the message completes
615  * or when an unrecoverable error occurs.
616  * @user_data: a pointer passed to @callback.
617  * 
618  * Queues the message @req for sending. All messages are processed while the
619  * glib main loop runs. If this %SoupMessage has been processed before, any
620  * resources related to the time it was last sent are freed.
621  *
622  * If the response %SoupDataBuffer has an owner of %SOUP_BUFFER_USER_OWNED, the
623  * message will not be queued, and @callback will be called with a
624  * %SoupErrorCode of %SOUP_ERROR_CANCELLED.
625  *
626  * Upon message completetion, the callback specified in @callback will be
627  * invoked. If after returning from this callback the message has not been
628  * requeued using %soup_message_queue, %soup_message_free will be called on
629  * @req.
630  */
631 void 
632 soup_message_queue (SoupMessage    *req,
633                     SoupCallbackFn  callback, 
634                     gpointer        user_data)
635 {
636         g_return_if_fail (req != NULL);
637         soup_queue_message (req, callback, user_data);
638 }
639
640 typedef struct {
641         SoupMessage *msg;
642         SoupAuth    *conn_auth;
643 } RequeueConnectData;
644
645 static void
646 requeue_connect_cb (SoupContext          *ctx,
647                     SoupConnectErrorCode  err,
648                     SoupConnection       *conn,
649                     gpointer              user_data)
650 {
651         RequeueConnectData *data = user_data;
652
653         if (conn && !conn->auth)
654                 conn->auth = data->conn_auth;
655         else
656                 soup_auth_free (data->conn_auth);
657
658         soup_queue_connect_cb (ctx, err, conn, data->msg);
659         if (data->msg->errorcode)
660                 soup_message_issue_callback (data->msg);
661
662         g_free (data);
663 }
664
665 static void
666 requeue_read_error (gboolean body_started, gpointer user_data)
667 {
668         RequeueConnectData *data = user_data;
669         SoupMessage *msg = data->msg;
670         SoupContext *dest_ctx = msg->connection->context;
671
672         soup_context_ref (dest_ctx);
673
674         soup_connection_set_keep_alive (msg->connection, FALSE);
675         soup_connection_release (msg->connection);
676         msg->connection = NULL;
677
678         soup_queue_message (msg, 
679                             msg->priv->callback, 
680                             msg->priv->user_data);
681
682         msg->status = SOUP_STATUS_CONNECTING;
683
684         msg->priv->connect_tag =
685                 soup_context_get_connection (dest_ctx, 
686                                              requeue_connect_cb, 
687                                              data);
688
689         soup_context_unref (dest_ctx);
690 }
691
692 static void
693 requeue_read_finished (const SoupDataBuffer *buf,
694                        gpointer        user_data)
695 {
696         RequeueConnectData *data = user_data;
697         SoupMessage *msg = data->msg;
698         SoupConnection *conn = msg->connection;
699
700         if (!soup_connection_is_keep_alive (msg->connection))
701                 requeue_read_error (FALSE, data);
702         else {
703                 g_free (data);
704                 msg->connection = NULL;
705
706                 soup_queue_message (msg, 
707                                     msg->priv->callback, 
708                                     msg->priv->user_data);
709
710                 msg->connection = conn;
711         }
712 }
713
714 /**
715  * soup_message_requeue:
716  * @req: a %SoupMessage
717  *
718  * This causes @req to be placed back on the queue to be attempted again.
719  **/
720 void
721 soup_message_requeue (SoupMessage *req)
722 {
723         g_return_if_fail (req != NULL);
724
725         if (req->connection && req->connection->auth && req->priv->read_tag) {
726                 RequeueConnectData *data = NULL;
727
728                 data = g_new0 (RequeueConnectData, 1);
729                 data->msg = req;
730                 data->conn_auth = req->connection->auth;
731
732                 soup_transfer_read_set_callbacks (req->priv->read_tag,
733                                                   NULL,
734                                                   NULL,
735                                                   requeue_read_finished,
736                                                   requeue_read_error,
737                                                   data);
738                 req->priv->read_tag = 0;
739         } else
740                 soup_queue_message (req, 
741                                     req->priv->callback, 
742                                     req->priv->user_data);
743 }
744
745 /**
746  * soup_message_send:
747  * @msg: a %SoupMessage.
748  * 
749  * Syncronously send @msg. This call will not return until the transfer is
750  * finished successfully or there is an unrecoverable error. 
751  *
752  * @msg is not free'd upon return.
753  *
754  * Return value: the %SoupErrorClass of the error encountered while sending or
755  * reading the response.
756  */
757 SoupErrorClass
758 soup_message_send (SoupMessage *msg)
759 {
760         soup_message_queue (msg, NULL, NULL);
761
762         while (1) {
763                 g_main_iteration (TRUE); 
764
765                 if (msg->status == SOUP_STATUS_FINISHED || 
766                     SOUP_ERROR_IS_TRANSPORT (msg->errorcode))
767                         break;
768
769                 /* Quit if soup_shutdown has been called */ 
770                 if (!soup_initialized)
771                         return SOUP_ERROR_CANCELLED;
772         }
773
774         return msg->errorclass;
775 }
776
777 static void
778 maybe_validate_auth (SoupMessage *msg, gpointer user_data)
779 {
780         gboolean proxy = GPOINTER_TO_INT (user_data);
781         int auth_failure;
782         SoupContext *ctx;
783         SoupAuth *auth;
784
785         if (proxy) {
786                 ctx = soup_get_proxy ();
787                 auth_failure = SOUP_ERROR_PROXY_UNAUTHORIZED; /* 407 */
788         }
789         else {
790                 ctx = msg->context;
791                 auth_failure = SOUP_ERROR_UNAUTHORIZED; /* 401 */
792         }
793
794         auth = soup_auth_lookup (ctx);
795         if (!auth)
796                 return;
797
798         if (msg->errorcode == auth_failure) {
799                 /* Pass through */
800         }
801         else if (msg->errorclass == SOUP_ERROR_CLASS_SERVER_ERROR) {
802                 /* 
803                  * We have no way of knowing whether our auth is any good
804                  * anymore, so just invalidate it and start from the
805                  * beginning next time.
806                  */
807                 soup_auth_invalidate (auth, ctx);
808         }
809         else {
810                 auth->status = SOUP_AUTH_STATUS_SUCCESSFUL;
811                 auth->controlling_msg = NULL;
812         }
813 } /* maybe_validate_auth */
814
815 static void 
816 authorize_handler (SoupMessage *msg, gboolean proxy)
817 {
818         const GSList *vals;
819         SoupAuth *auth;
820         SoupContext *ctx;
821         const SoupUri *uri;
822
823         if (msg->connection->auth &&
824             msg->connection->auth->status == SOUP_AUTH_STATUS_SUCCESSFUL)
825                 goto THROW_CANT_AUTHENTICATE;
826
827         ctx = proxy ? soup_get_proxy () : msg->context;
828         uri = soup_context_get_uri (ctx);
829
830         vals = soup_message_get_header_list (msg->response_headers, 
831                                              proxy ? 
832                                                      "Proxy-Authenticate" : 
833                                                      "WWW-Authenticate");
834         if (!vals) goto THROW_CANT_AUTHENTICATE;
835
836         auth = soup_auth_lookup (ctx);
837         if (auth) {
838                 g_assert (auth->status != SOUP_AUTH_STATUS_INVALID);
839
840                 if (auth->status == SOUP_AUTH_STATUS_PENDING) {
841                         if (auth->controlling_msg == msg) {
842                                 auth->status = SOUP_AUTH_STATUS_FAILED;
843                                 goto THROW_CANT_AUTHENTICATE;
844                         }
845                         else {
846                                 soup_message_requeue (msg);
847                                 return;
848                         }
849                 }
850                 else if (auth->status == SOUP_AUTH_STATUS_FAILED ||
851                          auth->status == SOUP_AUTH_STATUS_SUCCESSFUL) {
852                         /*
853                          * We've failed previously, but we'll give it
854                          * another go, or we've been successful
855                          * previously, but it's not working anymore.
856                          *
857                          * Invalidate the auth, so it's removed from the
858                          * hash and try it again as if we never had it
859                          * in the first place.
860                          */
861                         soup_auth_invalidate (auth, ctx);
862                         soup_message_requeue (msg);
863                         return;
864                 }
865         }
866
867         if (!auth) {
868                 auth = soup_auth_new_from_header_list (uri, vals);
869
870                 if (!auth) {
871                         soup_message_set_error_full (
872                                 msg, 
873                                 proxy ? 
874                                         SOUP_ERROR_CANT_AUTHENTICATE_PROXY : 
875                                         SOUP_ERROR_CANT_AUTHENTICATE,
876                                 proxy ? 
877                                         "Unknown authentication scheme "
878                                         "required by proxy" :
879                                         "Unknown authentication scheme "
880                                         "required");
881                         return;
882                 }
883
884                 auth->status = SOUP_AUTH_STATUS_PENDING;
885                 auth->controlling_msg = msg;
886                 soup_message_add_handler (msg, SOUP_HANDLER_PRE_BODY,
887                                           maybe_validate_auth,
888                                           GINT_TO_POINTER (proxy));
889         }
890
891         /*
892          * Call registered authenticate handler
893          */
894         if (!uri->user && soup_auth_fn)
895                 (*soup_auth_fn) (auth->type,
896                                  (SoupUri *) uri,
897                                  auth->realm, 
898                                  soup_auth_fn_user_data);
899
900         if (!uri->user) {
901                 soup_auth_free (auth);
902                 goto THROW_CANT_AUTHENTICATE;
903         }
904
905         /*
906          * Initialize with auth data (possibly returned from 
907          * auth callback).
908          */
909         soup_auth_initialize (auth, uri);
910
911         if (auth->type == SOUP_AUTH_TYPE_NTLM) {
912                 SoupAuth *old_auth = msg->connection->auth;
913
914                 if (old_auth)
915                         soup_auth_free (old_auth);
916                 msg->connection->auth = auth;
917         } else
918                 soup_auth_set_context (auth, ctx);
919
920         soup_message_requeue (msg);
921
922         return;
923
924  THROW_CANT_AUTHENTICATE:
925         soup_message_set_error (msg, 
926                                 proxy ? 
927                                         SOUP_ERROR_CANT_AUTHENTICATE_PROXY : 
928                                         SOUP_ERROR_CANT_AUTHENTICATE);
929 }
930
931 static void 
932 redirect_handler (SoupMessage *msg, gpointer user_data)
933 {
934         const gchar *new_loc;
935
936         if (msg->errorclass != SOUP_ERROR_CLASS_REDIRECT || 
937             msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT) return;
938
939         new_loc = soup_message_get_header (msg->response_headers, "Location");
940
941         if (new_loc) {
942                 const SoupUri *old_uri;
943                 SoupUri *new_uri;
944                 SoupContext *new_ctx;
945
946                 old_uri = soup_context_get_uri (msg->context);
947
948                 new_uri = soup_uri_new (new_loc);
949                 if (!new_uri) 
950                         goto INVALID_REDIRECT;
951
952                 /* 
953                  * Copy auth info from original URI.
954                  */
955                 if (old_uri->user && !new_uri->user)
956                         soup_uri_set_auth (new_uri,
957                                            old_uri->user, 
958                                            old_uri->passwd, 
959                                            old_uri->authmech);
960
961                 new_ctx = soup_context_from_uri (new_uri);
962
963                 soup_uri_free (new_uri);
964
965                 if (!new_ctx)
966                         goto INVALID_REDIRECT;
967
968                 soup_message_set_context (msg, new_ctx);
969                 soup_context_unref (new_ctx);
970
971                 soup_message_requeue (msg);
972         }
973
974         return;
975
976  INVALID_REDIRECT:
977         soup_message_set_error_full (msg, 
978                                      SOUP_ERROR_MALFORMED,
979                                      "Invalid Redirect URL");
980 }
981
982 typedef enum {
983         RESPONSE_HEADER_HANDLER = 1,
984         RESPONSE_ERROR_CODE_HANDLER,
985         RESPONSE_ERROR_CLASS_HANDLER
986 } SoupHandlerKind;
987
988 typedef struct {
989         SoupHandlerType   type;
990         SoupCallbackFn    handler_cb;
991         gpointer          user_data;
992
993         SoupHandlerKind   kind;
994         union {
995                 guint             errorcode;
996                 SoupErrorClass    errorclass;
997                 const gchar      *header;
998         } data;
999 } SoupHandlerData;
1000
1001 static SoupHandlerData global_handlers [] = {
1002         /* 
1003          * Handle redirect response codes 300, 301, 302, 303, and 305.
1004          */
1005         {
1006                 SOUP_HANDLER_PRE_BODY,
1007                 redirect_handler, 
1008                 NULL, 
1009                 RESPONSE_HEADER_HANDLER, 
1010                 { (guint) "Location" }
1011         },
1012         /* 
1013          * Handle authorization.
1014          */
1015         {
1016                 SOUP_HANDLER_PRE_BODY,
1017                 (SoupCallbackFn) authorize_handler, 
1018                 GINT_TO_POINTER (FALSE), 
1019                 RESPONSE_ERROR_CODE_HANDLER, 
1020                 { 401 }
1021         },
1022         /* 
1023          * Handle proxy authorization.
1024          */
1025         {
1026                 SOUP_HANDLER_PRE_BODY,
1027                 (SoupCallbackFn) authorize_handler, 
1028                 GINT_TO_POINTER (TRUE), 
1029                 RESPONSE_ERROR_CODE_HANDLER, 
1030                 { 407 }
1031         },
1032         { 0 }
1033 };
1034
1035 static inline void 
1036 run_handler (SoupMessage     *msg, 
1037              SoupHandlerType  invoke_type, 
1038              SoupHandlerData *data)
1039 {
1040         if (data->type != invoke_type) return;
1041
1042         switch (data->kind) {
1043         case RESPONSE_HEADER_HANDLER:
1044                 if (!soup_message_get_header (msg->response_headers,
1045                                               data->data.header))
1046                         return;
1047                 break;
1048         case RESPONSE_ERROR_CODE_HANDLER:
1049                 if (msg->errorcode != data->data.errorcode) return;
1050                 break;
1051         case RESPONSE_ERROR_CLASS_HANDLER:
1052                 if (msg->errorclass != data->data.errorclass) return;
1053                 break;
1054         default:
1055                 break;
1056         }
1057
1058         (*data->handler_cb) (msg, data->user_data);
1059 }
1060
1061 /*
1062  * Run each handler with matching criteria (first per-message then global
1063  * handlers). If a handler requeues a message, we stop processing and terminate
1064  * the current request. 
1065  *
1066  * After running all handlers, if there is an error set or the invoke type was
1067  * post_body, issue the final callback.  
1068  *
1069  * FIXME: If the errorcode is changed by a handler, we should restart the
1070  * processing.  
1071  */
1072 gboolean
1073 soup_message_run_handlers (SoupMessage *msg, SoupHandlerType invoke_type)
1074 {
1075         GSList *list;
1076         SoupHandlerData *data;
1077
1078         g_return_val_if_fail (msg != NULL, FALSE);
1079
1080         for (list = msg->priv->content_handlers; list; list = list->next) {
1081                 data = list->data;
1082
1083                 run_handler (msg, invoke_type, data);
1084
1085                 if (msg->status == SOUP_STATUS_QUEUED ||
1086                     msg->status == SOUP_STATUS_CONNECTING) return TRUE;
1087         }
1088
1089         for (data = global_handlers; data->type; data++) {
1090                 run_handler (msg, invoke_type, data);
1091
1092                 if (msg->status == SOUP_STATUS_QUEUED ||
1093                     msg->status == SOUP_STATUS_CONNECTING) return TRUE;
1094         }
1095
1096         /*
1097          * Issue final callback if the invoke_type is POST_BODY and the error
1098          * class is not INFORMATIONAL. 
1099          */
1100         if (invoke_type == SOUP_HANDLER_POST_BODY && 
1101             msg->errorclass != SOUP_ERROR_CLASS_INFORMATIONAL) {
1102                 soup_message_issue_callback (msg);
1103                 return TRUE;
1104         }
1105
1106         return FALSE;
1107 }
1108
1109 static void 
1110 add_handler (SoupMessage      *msg,
1111              SoupHandlerType   type,
1112              SoupCallbackFn    handler_cb,
1113              gpointer          user_data,
1114              SoupHandlerKind   kind,
1115              const gchar      *header,
1116              guint             errorcode,
1117              guint             errorclass)
1118 {
1119         SoupHandlerData *data;
1120
1121         data = g_new0 (SoupHandlerData, 1);
1122         data->type = type;
1123         data->handler_cb = handler_cb;
1124         data->user_data = user_data;
1125         data->kind = kind;
1126
1127         switch (kind) {
1128         case RESPONSE_HEADER_HANDLER:
1129                 data->data.header = header;
1130                 break;
1131         case RESPONSE_ERROR_CODE_HANDLER:
1132                 data->data.errorcode = errorcode;
1133                 break;
1134         case RESPONSE_ERROR_CLASS_HANDLER:
1135                 data->data.errorclass = errorclass;
1136                 break;
1137         default:
1138                 break;
1139         }
1140
1141         msg->priv->content_handlers = 
1142                 g_slist_append (msg->priv->content_handlers, data);
1143 }
1144
1145 void 
1146 soup_message_add_header_handler (SoupMessage      *msg,
1147                                  const gchar      *header,
1148                                  SoupHandlerType   type,
1149                                  SoupCallbackFn    handler_cb,
1150                                  gpointer          user_data)
1151 {
1152         g_return_if_fail (msg != NULL);
1153         g_return_if_fail (header != NULL);
1154         g_return_if_fail (handler_cb != NULL);
1155
1156         add_handler (msg, 
1157                      type, 
1158                      handler_cb, 
1159                      user_data, 
1160                      RESPONSE_HEADER_HANDLER, 
1161                      header, 
1162                      0,
1163                      0);
1164 }
1165
1166 void 
1167 soup_message_add_error_code_handler (SoupMessage      *msg,
1168                                      guint             errorcode,
1169                                      SoupHandlerType   type,
1170                                      SoupCallbackFn    handler_cb,
1171                                      gpointer          user_data)
1172 {
1173         g_return_if_fail (msg != NULL);
1174         g_return_if_fail (errorcode != 0);
1175         g_return_if_fail (handler_cb != NULL);
1176
1177         add_handler (msg, 
1178                      type, 
1179                      handler_cb, 
1180                      user_data, 
1181                      RESPONSE_ERROR_CODE_HANDLER, 
1182                      NULL, 
1183                      errorcode,
1184                      0);
1185 }
1186
1187 void 
1188 soup_message_add_error_class_handler (SoupMessage      *msg,
1189                                       SoupErrorClass    errorclass,
1190                                       SoupHandlerType   type,
1191                                       SoupCallbackFn    handler_cb,
1192                                       gpointer          user_data)
1193 {
1194         g_return_if_fail (msg != NULL);
1195         g_return_if_fail (errorclass != 0);
1196         g_return_if_fail (handler_cb != NULL);
1197
1198         add_handler (msg, 
1199                      type, 
1200                      handler_cb, 
1201                      user_data, 
1202                      RESPONSE_ERROR_CLASS_HANDLER, 
1203                      NULL, 
1204                      0,
1205                      errorclass);
1206 }
1207
1208 void 
1209 soup_message_add_handler (SoupMessage      *msg,
1210                           SoupHandlerType   type,
1211                           SoupCallbackFn    handler_cb,
1212                           gpointer          user_data)
1213 {
1214         g_return_if_fail (msg != NULL);
1215         g_return_if_fail (handler_cb != NULL);
1216
1217         add_handler (msg, 
1218                      type, 
1219                      handler_cb, 
1220                      user_data, 
1221                      0, 
1222                      NULL, 
1223                      0,
1224                      0);
1225 }
1226
1227 void
1228 soup_message_remove_handler (SoupMessage     *msg, 
1229                              SoupHandlerType  type,
1230                              SoupCallbackFn   handler_cb,
1231                              gpointer         user_data)
1232 {
1233         GSList *iter = msg->priv->content_handlers;
1234
1235         while (iter) {
1236                 SoupHandlerData *data = iter->data;
1237
1238                 if (data->handler_cb == handler_cb &&
1239                     data->user_data == user_data &&
1240                     data->type == type) {
1241                         msg->priv->content_handlers = 
1242                                 g_slist_remove_link (
1243                                         msg->priv->content_handlers,
1244                                         iter);
1245                         g_free (data);
1246                         break;
1247                 }
1248                 
1249                 iter = iter->next;
1250         }
1251 }
1252
1253 static inline gboolean
1254 ADDED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
1255 {
1256         return ((newflags & find) && !(msg->priv->msg_flags & find));
1257 }
1258
1259 static inline gboolean
1260 REMOVED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
1261 {
1262         return (!(newflags & find) && (msg->priv->msg_flags & find));
1263 }
1264
1265 void
1266 soup_message_set_flags (SoupMessage *msg, guint flags)
1267 {
1268         g_return_if_fail (msg != NULL);
1269
1270         msg->priv->msg_flags = flags;
1271 }
1272
1273 guint
1274 soup_message_get_flags (SoupMessage *msg)
1275 {
1276         g_return_val_if_fail (msg != NULL, 0);
1277
1278         return msg->priv->msg_flags;
1279 }
1280
1281 void 
1282 soup_message_set_http_version  (SoupMessage *msg, SoupHttpVersion version)
1283 {
1284         g_return_if_fail (msg != NULL);
1285
1286         msg->priv->http_version = version;
1287 }
1288
1289 SoupHttpVersion
1290 soup_message_get_http_version (SoupMessage *msg)
1291 {
1292         g_return_val_if_fail (msg != NULL, SOUP_HTTP_1_0);
1293
1294         return msg->priv->http_version;
1295 }
1296
1297 void
1298 soup_message_set_context (SoupMessage       *msg,
1299                           SoupContext       *new_ctx)
1300 {
1301         g_return_if_fail (msg != NULL);
1302
1303         if (msg->context)
1304                 soup_context_unref (msg->context);
1305
1306         if (new_ctx)
1307                 soup_context_ref (new_ctx);
1308
1309         msg->context = new_ctx;
1310 }
1311
1312 SoupContext *
1313 soup_message_get_context (SoupMessage       *msg)
1314 {
1315         g_return_val_if_fail (msg != NULL, NULL);
1316
1317         if (msg->context)
1318                 soup_context_ref (msg->context);
1319
1320         return msg->context;
1321 }
1322
1323 void
1324 soup_message_set_error (SoupMessage *msg, SoupKnownErrorCode errcode)
1325 {
1326         g_return_if_fail (msg != NULL);
1327         g_return_if_fail (errcode != 0);
1328
1329         g_free ((gchar *) msg->errorphrase);
1330
1331         msg->errorcode = errcode;
1332         msg->errorclass = soup_error_get_class (errcode);
1333         msg->errorphrase = g_strdup (soup_error_get_phrase (errcode));
1334 }
1335
1336 void
1337 soup_message_set_error_full (SoupMessage *msg, 
1338                              guint        errcode, 
1339                              const gchar *errphrase)
1340 {
1341         g_return_if_fail (msg != NULL);
1342         g_return_if_fail (errcode != 0);
1343         g_return_if_fail (errphrase != NULL);
1344
1345         g_free ((gchar *) msg->errorphrase);
1346
1347         msg->errorcode = errcode;
1348         msg->errorclass = soup_error_get_class (errcode);
1349         msg->errorphrase = g_strdup (errphrase);
1350 }
1351
1352 void
1353 soup_message_set_handler_error (SoupMessage *msg, 
1354                                 guint        errcode, 
1355                                 const gchar *errphrase)
1356 {
1357         g_return_if_fail (msg != NULL);
1358         g_return_if_fail (errcode != 0);
1359         g_return_if_fail (errphrase != NULL);
1360
1361         g_free ((gchar *) msg->errorphrase);
1362
1363         msg->errorcode = errcode;
1364         msg->errorclass = SOUP_ERROR_CLASS_HANDLER;
1365         msg->errorphrase = g_strdup (errphrase);
1366 }