If the new context points to a different server from the old context, call
[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@ximian.com)
7  *
8  * Copyright (C) 2000-2002, Ximian, 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_queue:
497  * @req: a %SoupMessage.
498  * @callback: a %SoupCallbackFn which will be called after the message completes
499  * or when an unrecoverable error occurs.
500  * @user_data: a pointer passed to @callback.
501  * 
502  * Queues the message @req for sending. All messages are processed while the
503  * glib main loop runs. If this %SoupMessage has been processed before, any
504  * resources related to the time it was last sent are freed.
505  *
506  * If the response %SoupDataBuffer has an owner of %SOUP_BUFFER_USER_OWNED, the
507  * message will not be queued, and @callback will be called with a
508  * %SoupErrorCode of %SOUP_ERROR_CANCELLED.
509  *
510  * Upon message completetion, the callback specified in @callback will be
511  * invoked. If after returning from this callback the message has not been
512  * requeued using %soup_message_queue, %soup_message_free will be called on
513  * @req.
514  */
515 void 
516 soup_message_queue (SoupMessage    *req,
517                     SoupCallbackFn  callback, 
518                     gpointer        user_data)
519 {
520         g_return_if_fail (req != NULL);
521         soup_queue_message (req, callback, user_data);
522 }
523
524 typedef struct {
525         SoupMessage *msg;
526         SoupAuth    *conn_auth;
527 } RequeueConnectData;
528
529 static void
530 requeue_connect_cb (SoupContext          *ctx,
531                     SoupConnectErrorCode  err,
532                     SoupConnection       *conn,
533                     gpointer              user_data)
534 {
535         RequeueConnectData *data = user_data;
536
537         if (conn && !conn->auth)
538                 conn->auth = data->conn_auth;
539         else
540                 soup_auth_free (data->conn_auth);
541
542         soup_queue_connect_cb (ctx, err, conn, data->msg);
543         if (data->msg->errorcode)
544                 soup_message_issue_callback (data->msg);
545
546         g_free (data);
547 }
548
549 static void
550 requeue_read_error (gboolean body_started, gpointer user_data)
551 {
552         RequeueConnectData *data = user_data;
553         SoupMessage *msg = data->msg;
554         SoupContext *dest_ctx = msg->connection->context;
555
556         soup_context_ref (dest_ctx);
557
558         soup_connection_set_keep_alive (msg->connection, FALSE);
559         soup_connection_release (msg->connection);
560         msg->connection = NULL;
561
562         soup_queue_message (msg, 
563                             msg->priv->callback, 
564                             msg->priv->user_data);
565
566         msg->status = SOUP_STATUS_CONNECTING;
567
568         msg->priv->connect_tag =
569                 soup_context_get_connection (dest_ctx, 
570                                              requeue_connect_cb, 
571                                              data);
572
573         soup_context_unref (dest_ctx);
574 }
575
576 static void
577 requeue_read_finished (const SoupDataBuffer *buf,
578                        gpointer        user_data)
579 {
580         RequeueConnectData *data = user_data;
581         SoupMessage *msg = data->msg;
582         SoupConnection *conn = msg->connection;
583
584         if (!soup_connection_is_keep_alive (msg->connection))
585                 requeue_read_error (FALSE, data);
586         else {
587                 g_free (data);
588                 msg->connection = NULL;
589
590                 soup_queue_message (msg, 
591                                     msg->priv->callback, 
592                                     msg->priv->user_data);
593
594                 msg->connection = conn;
595         }
596 }
597
598 /**
599  * soup_message_requeue:
600  * @req: a %SoupMessage
601  *
602  * This causes @req to be placed back on the queue to be attempted again.
603  **/
604 void
605 soup_message_requeue (SoupMessage *req)
606 {
607         g_return_if_fail (req != NULL);
608
609         if (req->connection && req->connection->auth && req->priv->read_tag) {
610                 RequeueConnectData *data = NULL;
611
612                 data = g_new0 (RequeueConnectData, 1);
613                 data->msg = req;
614                 data->conn_auth = req->connection->auth;
615
616                 soup_transfer_read_set_callbacks (req->priv->read_tag,
617                                                   NULL,
618                                                   NULL,
619                                                   requeue_read_finished,
620                                                   requeue_read_error,
621                                                   data);
622                 req->priv->read_tag = 0;
623         } else
624                 soup_queue_message (req, 
625                                     req->priv->callback, 
626                                     req->priv->user_data);
627 }
628
629 /**
630  * soup_message_send:
631  * @msg: a %SoupMessage.
632  * 
633  * Syncronously send @msg. This call will not return until the transfer is
634  * finished successfully or there is an unrecoverable error. 
635  *
636  * @msg is not free'd upon return.
637  *
638  * Return value: the %SoupErrorClass of the error encountered while sending or
639  * reading the response.
640  */
641 SoupErrorClass
642 soup_message_send (SoupMessage *msg)
643 {
644         soup_message_queue (msg, NULL, NULL);
645
646         while (1) {
647                 g_main_iteration (TRUE); 
648
649                 if (msg->status == SOUP_STATUS_FINISHED || 
650                     SOUP_ERROR_IS_TRANSPORT (msg->errorcode))
651                         break;
652
653                 /* Quit if soup_shutdown has been called */ 
654                 if (!soup_initialized)
655                         return SOUP_ERROR_CANCELLED;
656         }
657
658         return msg->errorclass;
659 }
660
661 static void
662 maybe_validate_auth (SoupMessage *msg, gpointer user_data)
663 {
664         gboolean proxy = GPOINTER_TO_INT (user_data);
665         int auth_failure;
666         SoupContext *ctx;
667         SoupAuth *auth;
668
669         if (proxy) {
670                 ctx = soup_get_proxy ();
671                 auth_failure = SOUP_ERROR_PROXY_UNAUTHORIZED; /* 407 */
672         }
673         else {
674                 ctx = msg->context;
675                 auth_failure = SOUP_ERROR_UNAUTHORIZED; /* 401 */
676         }
677
678         auth = soup_auth_lookup (ctx);
679         if (!auth)
680                 return;
681
682         if (msg->errorcode == auth_failure) {
683                 /* Pass through */
684         }
685         else if (msg->errorclass == SOUP_ERROR_CLASS_SERVER_ERROR) {
686                 /* 
687                  * We have no way of knowing whether our auth is any good
688                  * anymore, so just invalidate it and start from the
689                  * beginning next time.
690                  */
691                 soup_auth_invalidate (auth, ctx);
692         }
693         else {
694                 auth->status = SOUP_AUTH_STATUS_SUCCESSFUL;
695                 auth->controlling_msg = NULL;
696         }
697 } /* maybe_validate_auth */
698
699 static void 
700 authorize_handler (SoupMessage *msg, gboolean proxy)
701 {
702         const GSList *vals;
703         SoupAuth *auth;
704         SoupContext *ctx;
705         const SoupUri *uri;
706
707         if (msg->connection->auth &&
708             msg->connection->auth->status == SOUP_AUTH_STATUS_SUCCESSFUL)
709                 goto THROW_CANT_AUTHENTICATE;
710
711         ctx = proxy ? soup_get_proxy () : msg->context;
712         uri = soup_context_get_uri (ctx);
713
714         vals = soup_message_get_header_list (msg->response_headers, 
715                                              proxy ? 
716                                                      "Proxy-Authenticate" : 
717                                                      "WWW-Authenticate");
718         if (!vals) goto THROW_CANT_AUTHENTICATE;
719
720         auth = soup_auth_lookup (ctx);
721         if (auth) {
722                 g_assert (auth->status != SOUP_AUTH_STATUS_INVALID);
723
724                 if (auth->status == SOUP_AUTH_STATUS_PENDING) {
725                         if (auth->controlling_msg == msg) {
726                                 auth->status = SOUP_AUTH_STATUS_FAILED;
727                                 goto THROW_CANT_AUTHENTICATE;
728                         }
729                         else {
730                                 soup_message_requeue (msg);
731                                 return;
732                         }
733                 }
734                 else if (auth->status == SOUP_AUTH_STATUS_FAILED ||
735                          auth->status == SOUP_AUTH_STATUS_SUCCESSFUL) {
736                         /*
737                          * We've failed previously, but we'll give it
738                          * another go, or we've been successful
739                          * previously, but it's not working anymore.
740                          *
741                          * Invalidate the auth, so it's removed from the
742                          * hash and try it again as if we never had it
743                          * in the first place.
744                          */
745                         soup_auth_invalidate (auth, ctx);
746                         soup_message_requeue (msg);
747                         return;
748                 }
749         }
750
751         if (!auth) {
752                 auth = soup_auth_new_from_header_list (uri, vals);
753
754                 if (!auth) {
755                         soup_message_set_error_full (
756                                 msg, 
757                                 proxy ? 
758                                         SOUP_ERROR_CANT_AUTHENTICATE_PROXY : 
759                                         SOUP_ERROR_CANT_AUTHENTICATE,
760                                 proxy ? 
761                                         "Unknown authentication scheme "
762                                         "required by proxy" :
763                                         "Unknown authentication scheme "
764                                         "required");
765                         return;
766                 }
767
768                 auth->status = SOUP_AUTH_STATUS_PENDING;
769                 auth->controlling_msg = msg;
770                 soup_message_add_handler (msg, SOUP_HANDLER_PRE_BODY,
771                                           maybe_validate_auth,
772                                           GINT_TO_POINTER (proxy));
773         }
774
775         /*
776          * Call registered authenticate handler
777          */
778         if (!uri->user && soup_auth_fn)
779                 (*soup_auth_fn) (auth->type,
780                                  (SoupUri *) uri,
781                                  auth->realm, 
782                                  soup_auth_fn_user_data);
783
784         if (!uri->user) {
785                 soup_auth_free (auth);
786                 goto THROW_CANT_AUTHENTICATE;
787         }
788
789         /*
790          * Initialize with auth data (possibly returned from 
791          * auth callback).
792          */
793         soup_auth_initialize (auth, uri);
794
795         if (auth->type == SOUP_AUTH_TYPE_NTLM) {
796                 SoupAuth *old_auth = msg->connection->auth;
797
798                 if (old_auth)
799                         soup_auth_free (old_auth);
800                 msg->connection->auth = auth;
801         } else
802                 soup_auth_set_context (auth, ctx);
803
804         soup_message_requeue (msg);
805
806         return;
807
808  THROW_CANT_AUTHENTICATE:
809         soup_message_set_error (msg, 
810                                 proxy ? 
811                                         SOUP_ERROR_CANT_AUTHENTICATE_PROXY : 
812                                         SOUP_ERROR_CANT_AUTHENTICATE);
813 }
814
815 static void 
816 redirect_handler (SoupMessage *msg, gpointer user_data)
817 {
818         const gchar *new_loc;
819
820         if (msg->errorclass != SOUP_ERROR_CLASS_REDIRECT || 
821             msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT) return;
822
823         new_loc = soup_message_get_header (msg->response_headers, "Location");
824
825         if (new_loc) {
826                 const SoupUri *old_uri;
827                 SoupUri *new_uri;
828                 SoupContext *new_ctx;
829
830                 old_uri = soup_context_get_uri (msg->context);
831
832                 new_uri = soup_uri_new (new_loc);
833                 if (!new_uri) 
834                         goto INVALID_REDIRECT;
835
836                 /* 
837                  * Copy auth info from original URI.
838                  */
839                 if (old_uri->user && !new_uri->user)
840                         soup_uri_set_auth (new_uri,
841                                            old_uri->user, 
842                                            old_uri->passwd, 
843                                            old_uri->authmech);
844
845                 new_ctx = soup_context_from_uri (new_uri);
846
847                 soup_uri_free (new_uri);
848
849                 if (!new_ctx)
850                         goto INVALID_REDIRECT;
851
852                 soup_message_set_context (msg, new_ctx);
853                 soup_context_unref (new_ctx);
854
855                 soup_message_requeue (msg);
856         }
857
858         return;
859
860  INVALID_REDIRECT:
861         soup_message_set_error_full (msg, 
862                                      SOUP_ERROR_MALFORMED,
863                                      "Invalid Redirect URL");
864 }
865
866 typedef enum {
867         RESPONSE_HEADER_HANDLER = 1,
868         RESPONSE_ERROR_CODE_HANDLER,
869         RESPONSE_ERROR_CLASS_HANDLER
870 } SoupHandlerKind;
871
872 typedef struct {
873         SoupHandlerType   type;
874         SoupCallbackFn    handler_cb;
875         gpointer          user_data;
876
877         SoupHandlerKind   kind;
878         union {
879                 guint             errorcode;
880                 SoupErrorClass    errorclass;
881                 const gchar      *header;
882         } data;
883 } SoupHandlerData;
884
885 static SoupHandlerData global_handlers [] = {
886         /* 
887          * Handle redirect response codes 300, 301, 302, 303, and 305.
888          */
889         {
890                 SOUP_HANDLER_PRE_BODY,
891                 redirect_handler, 
892                 NULL, 
893                 RESPONSE_HEADER_HANDLER, 
894                 { (guint) "Location" }
895         },
896         /* 
897          * Handle authorization.
898          */
899         {
900                 SOUP_HANDLER_PRE_BODY,
901                 (SoupCallbackFn) authorize_handler, 
902                 GINT_TO_POINTER (FALSE), 
903                 RESPONSE_ERROR_CODE_HANDLER, 
904                 { 401 }
905         },
906         /* 
907          * Handle proxy authorization.
908          */
909         {
910                 SOUP_HANDLER_PRE_BODY,
911                 (SoupCallbackFn) authorize_handler, 
912                 GINT_TO_POINTER (TRUE), 
913                 RESPONSE_ERROR_CODE_HANDLER, 
914                 { 407 }
915         },
916         { 0 }
917 };
918
919 static inline void 
920 run_handler (SoupMessage     *msg, 
921              SoupHandlerType  invoke_type, 
922              SoupHandlerData *data)
923 {
924         if (data->type != invoke_type) return;
925
926         switch (data->kind) {
927         case RESPONSE_HEADER_HANDLER:
928                 if (!soup_message_get_header (msg->response_headers,
929                                               data->data.header))
930                         return;
931                 break;
932         case RESPONSE_ERROR_CODE_HANDLER:
933                 if (msg->errorcode != data->data.errorcode) return;
934                 break;
935         case RESPONSE_ERROR_CLASS_HANDLER:
936                 if (msg->errorclass != data->data.errorclass) return;
937                 break;
938         default:
939                 break;
940         }
941
942         (*data->handler_cb) (msg, data->user_data);
943 }
944
945 /*
946  * Run each handler with matching criteria (first per-message then global
947  * handlers). If a handler requeues a message, we stop processing and terminate
948  * the current request. 
949  *
950  * After running all handlers, if there is an error set or the invoke type was
951  * post_body, issue the final callback.  
952  *
953  * FIXME: If the errorcode is changed by a handler, we should restart the
954  * processing.  
955  */
956 gboolean
957 soup_message_run_handlers (SoupMessage *msg, SoupHandlerType invoke_type)
958 {
959         GSList *list;
960         SoupHandlerData *data;
961
962         g_return_val_if_fail (msg != NULL, FALSE);
963
964         for (list = msg->priv->content_handlers; list; list = list->next) {
965                 data = list->data;
966
967                 run_handler (msg, invoke_type, data);
968
969                 if (msg->status == SOUP_STATUS_QUEUED ||
970                     msg->status == SOUP_STATUS_CONNECTING) return TRUE;
971         }
972
973         for (data = global_handlers; data->type; data++) {
974                 run_handler (msg, invoke_type, data);
975
976                 if (msg->status == SOUP_STATUS_QUEUED ||
977                     msg->status == SOUP_STATUS_CONNECTING) return TRUE;
978         }
979
980         /*
981          * Issue final callback if the invoke_type is POST_BODY and the error
982          * class is not INFORMATIONAL. 
983          */
984         if (invoke_type == SOUP_HANDLER_POST_BODY && 
985             msg->errorclass != SOUP_ERROR_CLASS_INFORMATIONAL) {
986                 soup_message_issue_callback (msg);
987                 return TRUE;
988         }
989
990         return FALSE;
991 }
992
993 static void 
994 add_handler (SoupMessage      *msg,
995              SoupHandlerType   type,
996              SoupCallbackFn    handler_cb,
997              gpointer          user_data,
998              SoupHandlerKind   kind,
999              const gchar      *header,
1000              guint             errorcode,
1001              guint             errorclass)
1002 {
1003         SoupHandlerData *data;
1004
1005         data = g_new0 (SoupHandlerData, 1);
1006         data->type = type;
1007         data->handler_cb = handler_cb;
1008         data->user_data = user_data;
1009         data->kind = kind;
1010
1011         switch (kind) {
1012         case RESPONSE_HEADER_HANDLER:
1013                 data->data.header = header;
1014                 break;
1015         case RESPONSE_ERROR_CODE_HANDLER:
1016                 data->data.errorcode = errorcode;
1017                 break;
1018         case RESPONSE_ERROR_CLASS_HANDLER:
1019                 data->data.errorclass = errorclass;
1020                 break;
1021         default:
1022                 break;
1023         }
1024
1025         msg->priv->content_handlers = 
1026                 g_slist_append (msg->priv->content_handlers, data);
1027 }
1028
1029 void 
1030 soup_message_add_header_handler (SoupMessage      *msg,
1031                                  const gchar      *header,
1032                                  SoupHandlerType   type,
1033                                  SoupCallbackFn    handler_cb,
1034                                  gpointer          user_data)
1035 {
1036         g_return_if_fail (msg != NULL);
1037         g_return_if_fail (header != NULL);
1038         g_return_if_fail (handler_cb != NULL);
1039
1040         add_handler (msg, 
1041                      type, 
1042                      handler_cb, 
1043                      user_data, 
1044                      RESPONSE_HEADER_HANDLER, 
1045                      header, 
1046                      0,
1047                      0);
1048 }
1049
1050 void 
1051 soup_message_add_error_code_handler (SoupMessage      *msg,
1052                                      guint             errorcode,
1053                                      SoupHandlerType   type,
1054                                      SoupCallbackFn    handler_cb,
1055                                      gpointer          user_data)
1056 {
1057         g_return_if_fail (msg != NULL);
1058         g_return_if_fail (errorcode != 0);
1059         g_return_if_fail (handler_cb != NULL);
1060
1061         add_handler (msg, 
1062                      type, 
1063                      handler_cb, 
1064                      user_data, 
1065                      RESPONSE_ERROR_CODE_HANDLER, 
1066                      NULL, 
1067                      errorcode,
1068                      0);
1069 }
1070
1071 void 
1072 soup_message_add_error_class_handler (SoupMessage      *msg,
1073                                       SoupErrorClass    errorclass,
1074                                       SoupHandlerType   type,
1075                                       SoupCallbackFn    handler_cb,
1076                                       gpointer          user_data)
1077 {
1078         g_return_if_fail (msg != NULL);
1079         g_return_if_fail (errorclass != 0);
1080         g_return_if_fail (handler_cb != NULL);
1081
1082         add_handler (msg, 
1083                      type, 
1084                      handler_cb, 
1085                      user_data, 
1086                      RESPONSE_ERROR_CLASS_HANDLER, 
1087                      NULL, 
1088                      0,
1089                      errorclass);
1090 }
1091
1092 void 
1093 soup_message_add_handler (SoupMessage      *msg,
1094                           SoupHandlerType   type,
1095                           SoupCallbackFn    handler_cb,
1096                           gpointer          user_data)
1097 {
1098         g_return_if_fail (msg != NULL);
1099         g_return_if_fail (handler_cb != NULL);
1100
1101         add_handler (msg, 
1102                      type, 
1103                      handler_cb, 
1104                      user_data, 
1105                      0, 
1106                      NULL, 
1107                      0,
1108                      0);
1109 }
1110
1111 void
1112 soup_message_remove_handler (SoupMessage     *msg, 
1113                              SoupHandlerType  type,
1114                              SoupCallbackFn   handler_cb,
1115                              gpointer         user_data)
1116 {
1117         GSList *iter = msg->priv->content_handlers;
1118
1119         while (iter) {
1120                 SoupHandlerData *data = iter->data;
1121
1122                 if (data->handler_cb == handler_cb &&
1123                     data->user_data == user_data &&
1124                     data->type == type) {
1125                         msg->priv->content_handlers = 
1126                                 g_slist_remove_link (
1127                                         msg->priv->content_handlers,
1128                                         iter);
1129                         g_free (data);
1130                         break;
1131                 }
1132                 
1133                 iter = iter->next;
1134         }
1135 }
1136
1137 static inline gboolean
1138 ADDED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
1139 {
1140         return ((newflags & find) && !(msg->priv->msg_flags & find));
1141 }
1142
1143 static inline gboolean
1144 REMOVED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
1145 {
1146         return (!(newflags & find) && (msg->priv->msg_flags & find));
1147 }
1148
1149 void
1150 soup_message_set_flags (SoupMessage *msg, guint flags)
1151 {
1152         g_return_if_fail (msg != NULL);
1153
1154         msg->priv->msg_flags = flags;
1155 }
1156
1157 guint
1158 soup_message_get_flags (SoupMessage *msg)
1159 {
1160         g_return_val_if_fail (msg != NULL, 0);
1161
1162         return msg->priv->msg_flags;
1163 }
1164
1165 void 
1166 soup_message_set_http_version  (SoupMessage *msg, SoupHttpVersion version)
1167 {
1168         g_return_if_fail (msg != NULL);
1169
1170         msg->priv->http_version = version;
1171 }
1172
1173 SoupHttpVersion
1174 soup_message_get_http_version (SoupMessage *msg)
1175 {
1176         g_return_val_if_fail (msg != NULL, SOUP_HTTP_1_0);
1177
1178         return msg->priv->http_version;
1179 }
1180
1181 void
1182 soup_message_set_context (SoupMessage       *msg,
1183                           SoupContext       *new_ctx)
1184 {
1185         g_return_if_fail (msg != NULL);
1186
1187         if (msg->context) {
1188                 if (msg->connection &&
1189                     (!new_ctx || (msg->context->server != new_ctx->server)))
1190                         soup_message_cleanup (msg);
1191                 soup_context_unref (msg->context);
1192         }
1193
1194         if (new_ctx)
1195                 soup_context_ref (new_ctx);
1196
1197         msg->context = new_ctx;
1198 }
1199
1200 SoupContext *
1201 soup_message_get_context (SoupMessage       *msg)
1202 {
1203         g_return_val_if_fail (msg != NULL, NULL);
1204
1205         if (msg->context)
1206                 soup_context_ref (msg->context);
1207
1208         return msg->context;
1209 }
1210
1211 void
1212 soup_message_set_error (SoupMessage *msg, SoupKnownErrorCode errcode)
1213 {
1214         g_return_if_fail (msg != NULL);
1215         g_return_if_fail (errcode != 0);
1216
1217         g_free ((gchar *) msg->errorphrase);
1218
1219         msg->errorcode = errcode;
1220         msg->errorclass = soup_error_get_class (errcode);
1221         msg->errorphrase = g_strdup (soup_error_get_phrase (errcode));
1222 }
1223
1224 void
1225 soup_message_set_error_full (SoupMessage *msg, 
1226                              guint        errcode, 
1227                              const gchar *errphrase)
1228 {
1229         g_return_if_fail (msg != NULL);
1230         g_return_if_fail (errcode != 0);
1231         g_return_if_fail (errphrase != NULL);
1232
1233         g_free ((gchar *) msg->errorphrase);
1234
1235         msg->errorcode = errcode;
1236         msg->errorclass = soup_error_get_class (errcode);
1237         msg->errorphrase = g_strdup (errphrase);
1238 }
1239
1240 void
1241 soup_message_set_handler_error (SoupMessage *msg, 
1242                                 guint        errcode, 
1243                                 const gchar *errphrase)
1244 {
1245         g_return_if_fail (msg != NULL);
1246         g_return_if_fail (errcode != 0);
1247         g_return_if_fail (errphrase != NULL);
1248
1249         g_free ((gchar *) msg->errorphrase);
1250
1251         msg->errorcode = errcode;
1252         msg->errorclass = SOUP_ERROR_CLASS_HANDLER;
1253         msg->errorphrase = g_strdup (errphrase);
1254 }