Add death_tag to _SoupConnection.
[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 SOAP 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         g_return_val_if_fail (context, NULL);
39
40         ret          = g_new0 (SoupMessage, 1);
41         ret->priv    = g_new0 (SoupMessagePrivate, 1);
42         ret->status  = SOUP_STATUS_IDLE;
43         ret->context = context;
44         ret->method  = method ? method : SOUP_METHOD_GET;
45
46         ret->request_headers = g_hash_table_new (soup_str_case_hash, 
47                                                  soup_str_case_equal);
48
49         ret->response_headers = g_hash_table_new (soup_str_case_hash, 
50                                                   soup_str_case_equal);
51
52         ret->priv->http_version = SOUP_HTTP_1_1;
53
54         soup_context_ref (context);
55
56         return ret;
57 }
58
59 /**
60  * soup_message_new_full:
61  * @context: a %SoupContext for the destination endpoint.
62  * @method: a string which will be used as the HTTP method for the created
63  * request, if NULL a GET request will be made..
64  * @req_owner: the %SoupOwnership of the passed data buffer.
65  * @req_body: a data buffer containing the body of the message request.
66  * @req_length: the byte length of @req_body.
67  * 
68  * Creates a new %SoupMessage, which will connect to the URL represented by
69  * @context.  A reference is added to @context.  The request data
70  * buffer will be filled from @req_owner, @req_body, and @req_length
71  * respectively.
72  *
73  * The new message has a status of @SOUP_STATUS_IDLE.
74  *
75  * Return value: the new %SoupMessage.
76  */
77 SoupMessage *
78 soup_message_new_full (SoupContext   *context,
79                        const gchar   *method,
80                        SoupOwnership  req_owner,
81                        gchar         *req_body,
82                        gulong         req_length)
83 {
84         SoupMessage *ret = soup_message_new (context, method);
85
86         ret->request.owner = req_owner;
87         ret->request.body = req_body;
88         ret->request.length = req_length;
89
90         return ret;
91 }
92
93 /**
94  * soup_message_cleanup:
95  * @req: a %SoupMessage.
96  * 
97  * Frees any temporary resources created in the processing of @req.  Also
98  * releases the active connection, if one exists. Request and response data
99  * buffers are left intact. 
100  */
101 void 
102 soup_message_cleanup (SoupMessage *req)
103 {
104         g_return_if_fail (req != NULL);
105
106         if (req->priv->read_tag) {
107                 soup_transfer_read_cancel (req->priv->read_tag);
108                 req->priv->read_tag = 0;
109         }
110
111         if (req->priv->write_tag) {
112                 soup_transfer_write_cancel (req->priv->write_tag);
113                 req->priv->write_tag = 0;
114         }
115
116         if (req->priv->connect_tag) {
117                 soup_context_cancel_connect (req->priv->connect_tag);
118                 req->priv->connect_tag = NULL;
119         }
120         if (req->connection) {
121                 soup_connection_release (req->connection);
122                 req->connection = NULL;
123         }
124
125         soup_active_requests = g_slist_remove (soup_active_requests, req);
126 }
127
128 static void
129 finalize_message (SoupMessage *req)
130 {
131         soup_context_unref (req->context);
132
133         if (req->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
134                 g_free (req->request.body);
135         if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
136                 g_free (req->response.body);
137
138         if (req->priv->req_header) 
139                 g_string_free (req->priv->req_header, TRUE);
140
141         soup_message_clear_headers (req->request_headers);
142         g_hash_table_destroy (req->request_headers);
143
144         soup_message_clear_headers (req->response_headers);
145         g_hash_table_destroy (req->response_headers);
146
147         g_slist_foreach (req->priv->content_handlers, (GFunc) g_free, NULL);
148         g_slist_free (req->priv->content_handlers);
149
150         g_free ((gchar *) req->errorphrase);
151         g_free (req->priv);
152         g_free (req);
153 }
154
155 /**
156  * soup_message_free:
157  * @req: a %SoupMessage to destroy.
158  * 
159  * Destroys the %SoupMessage pointed to by @req. Request and response headers
160  * are freed. Request and response data buffers are also freed if their
161  * ownership is %SOUP_BUFFER_SYSTEM_OWNED. The message's destination context
162  * will be de-referenced.
163  */
164 void 
165 soup_message_free (SoupMessage *req)
166 {
167         g_return_if_fail (req != NULL);
168
169         soup_message_cleanup (req);
170
171         finalize_message (req);
172 }
173
174 /**
175  * soup_message_issue_callback:
176  * @req: a %SoupMessage currently being processed.
177  * @error: a %SoupErrorCode to be passed to %req's completion callback.
178  * 
179  * Finalizes the message request, by first freeing any temporary resources, then
180  * issuing the callback function pointer passed in %soup_message_new or
181  * %soup_message_new_full. If, after returning from the callback, the message
182  * has not been requeued, @msg is destroyed using %soup_message_free.
183  */
184 void
185 soup_message_issue_callback (SoupMessage *req)
186 {
187         g_return_if_fail (req != NULL);
188
189         /* 
190          * Make sure we don't have some icky recursion if the callback 
191          * runs the main loop, and the connection has some data or error 
192          * which causes the callback to be run again.
193          */
194         soup_message_cleanup (req);
195
196         if (req->priv->callback) {
197                 (*req->priv->callback) (req, req->priv->user_data);
198
199                 if (req->status != SOUP_STATUS_QUEUED)
200                         finalize_message (req);
201         }
202 }
203
204 /**
205  * soup_message_cancel:
206  * @req: a %SoupMessage currently being processed.
207  * 
208  * Cancel a running message, and issue completion callback with a
209  * %SoupTransferStatus of %SOUP_ERROR_CANCELLED. If not requeued by the
210  * completion callback, the @msg will be destroyed.
211  */
212 void 
213 soup_message_cancel (SoupMessage *msg) 
214 {
215         soup_message_set_error (msg, SOUP_ERROR_CANCELLED);
216         soup_message_issue_callback (msg);
217 }
218
219 static gboolean 
220 foreach_free_header_list (gchar *name, GSList *vals, gpointer notused)
221 {
222         g_free (name);
223         g_slist_foreach (vals, (GFunc) g_free, NULL);
224         g_slist_free (vals);
225
226         return TRUE;
227 }
228
229 void
230 soup_message_clear_headers       (GHashTable        *hash)
231 {
232         g_return_if_fail (hash != NULL);
233
234         g_hash_table_foreach_remove (hash, 
235                                      (GHRFunc) foreach_free_header_list, 
236                                      NULL);
237 }
238
239 void 
240 soup_message_remove_header (GHashTable  *hash,
241                             const gchar *name)
242 {
243         gchar *stored_key;
244         GSList *vals;
245
246         g_return_if_fail (hash != NULL);
247         g_return_if_fail (name != NULL || name [0] != '\0');
248
249         if (g_hash_table_lookup_extended (hash, 
250                                           name, 
251                                           (gpointer *) &stored_key, 
252                                           (gpointer *) &vals)) {
253                 g_hash_table_remove (hash, name);
254                 foreach_free_header_list (stored_key, vals, NULL);
255         }
256 }
257
258 void 
259 soup_message_add_header (GHashTable  *hash,
260                          const gchar *name,
261                          const gchar *value) 
262 {
263         GSList *old_value;
264
265         g_return_if_fail (hash != NULL);
266         g_return_if_fail (name != NULL || name [0] != '\0');
267         g_return_if_fail (value != NULL);
268
269         old_value = g_hash_table_lookup (hash, name);
270
271         if (old_value)
272                 g_slist_append (old_value, g_strdup (value));
273         else
274                 g_hash_table_insert (hash, 
275                                      g_strdup (name), 
276                                      g_slist_append (NULL, 
277                                                      g_strdup (value)));
278 }
279
280 /**
281  * soup_message_get_header:
282  * @req: a %SoupMessage.
283  * @name: header name.
284  * 
285  * Lookup the first transport header with a key equal to @name.
286  *
287  * Return value: the header's value or NULL if not found.
288  */
289 const gchar *
290 soup_message_get_header (GHashTable *hash,
291                          const gchar *name)
292 {
293         GSList *vals;
294
295         g_return_val_if_fail (hash != NULL, NULL);
296         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);  
297
298         vals = g_hash_table_lookup (hash, name);
299         if (vals) 
300                 return vals->data;
301
302         return NULL;
303 }
304
305 /**
306  * soup_message_get_header_list:
307  * @req: a %SoupMessage.
308  * @name: header name.
309  * 
310  * Lookup the all transport request headers with a key equal to @name.
311  *
312  * Return value: a const pointer to a GSList of header values or NULL if not
313  * found.  
314  */
315 const GSList *
316 soup_message_get_header_list (GHashTable  *hash,
317                               const gchar *name)
318 {
319         g_return_val_if_fail (hash != NULL, NULL);
320         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);  
321
322         return g_hash_table_lookup (hash, name);
323 }
324
325 typedef struct {
326         GHFunc   func;
327         gpointer user_data;
328 } ForeachData;
329
330 static void 
331 foreach_value_in_list (gchar *name, GSList *vals, ForeachData *data)
332 {
333         while (vals) {
334                 gchar *v = vals->data;
335
336                 (*data->func) (name, v, data->user_data);
337
338                 vals = vals->next;
339         }
340 }
341
342 void
343 soup_message_foreach_header      (GHashTable        *hash,
344                                   GHFunc             func,
345                                   gpointer           user_data)
346 {
347         ForeachData data = { func, user_data };
348
349         g_return_if_fail (hash != NULL);
350         g_return_if_fail (func != NULL);
351
352         g_hash_table_foreach (hash, (GHFunc) foreach_value_in_list, &data);
353 }
354
355 typedef struct {
356         GHRFunc   func;
357         gpointer user_data;
358 } ForeachRemoveData;
359
360 static gboolean 
361 foreach_remove_value_in_list (gchar             *name, 
362                               GSList            *vals, 
363                               ForeachRemoveData *data)
364 {
365         GSList *iter = vals;
366
367         while (iter) {
368                 gchar *v = iter->data;
369                 gboolean ret = FALSE;
370
371                 ret = (*data->func) (name, v, data->user_data);
372                 if (ret) {
373                         GSList *next = iter->next;
374
375                         vals = g_slist_remove (vals, v);
376                         g_free (v);
377
378                         iter = next;
379                 } else
380                         iter = iter->next;
381         }
382
383         if (!vals) {
384                 g_free (name);
385                 return TRUE;
386         } 
387
388         return FALSE;
389 }
390
391 void
392 soup_message_foreach_remove_header (GHashTable        *hash,
393                                     GHRFunc            func,
394                                     gpointer           user_data)
395 {
396         ForeachRemoveData data = { func, user_data };
397
398         g_return_if_fail (hash != NULL);
399         g_return_if_fail (func != NULL);
400
401         g_hash_table_foreach_remove (hash, 
402                                      (GHRFunc) foreach_remove_value_in_list, 
403                                      &data);
404 }
405
406 /**
407  * soup_message_set_request_header:
408  * @req: a %SoupMessage.
409  * @name: header name.
410  * @value: header value.
411  *
412  * ** DEPRECATED **
413  * 
414  * Adds a new transport header to be sent on an outgoing request. Passing a NULL
415  * @value will remove all headers with a name equal to @name.
416  */
417 void
418 soup_message_set_request_header (SoupMessage *req,
419                                  const gchar *name,
420                                  const gchar *value) 
421 {
422         g_return_if_fail (req != NULL);
423         g_return_if_fail (name != NULL || name [0] != '\0');
424
425         g_warning ("soup_message_set_request_header is DEPRECATED. Use "
426                    "soup_message_add_header, with msg->request_headers as "
427                    "the first argument.\n");
428
429         soup_message_add_header (req->request_headers, name, value);
430 }
431
432 /**
433  * soup_message_get_request_header:
434  * @req: a %SoupMessage.
435  * @name: header name.
436  * 
437  * ** DEPRECATED **
438  * 
439  * Lookup the first transport request header with a key equal to @name.
440  *
441  * Return value: the header's value or NULL if not found.
442  */
443 const gchar *
444 soup_message_get_request_header (SoupMessage *req,
445                                  const gchar *name) 
446 {
447         GSList *vals;
448         g_return_val_if_fail (req != NULL, NULL);
449         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
450
451         g_warning ("soup_message_get_request_header is DEPRECATED. Use "
452                    "soup_message_get_header, with msg->request_headers as "
453                    "the first argument.\n");
454
455         if (req->request_headers) {
456                 vals = g_hash_table_lookup (req->request_headers, name);
457                 if (vals) 
458                         return vals->data;
459         }
460
461         return NULL;
462 }
463
464 /**
465  * soup_message_set_response_header:
466  * @req: a %SoupMessage.
467  * @name: header name.
468  * @value: header value.
469  * 
470  * ** DEPRECATED **
471  * 
472  * Adds a new transport header to be sent on an outgoing response. Passing a
473  * NULL @value will remove all headers with a name equal to @name.
474  */
475 void
476 soup_message_set_response_header (SoupMessage *req,
477                                   const gchar *name,
478                                   const gchar *value) 
479 {
480         g_return_if_fail (req != NULL);
481         g_return_if_fail (name != NULL || name [0] != '\0');
482
483         g_warning ("soup_message_set_response_header is DEPRECATED. Use "
484                    "soup_message_add_header, with msg->response_headers as "
485                    "the first argument.\n");
486
487         soup_message_add_header (req->response_headers, name, value);
488 }
489
490 /**
491  * soup_message_get_response_header:
492  * @req: a %SoupMessage.
493  * @name: header name.
494  * 
495  * ** DEPRECATED **
496  * 
497  * Lookup the transport response header with a key equal to @name.
498  *
499  * Return value: the header's value or NULL if not found.
500  */
501 const gchar *
502 soup_message_get_response_header (SoupMessage *req,
503                                   const gchar *name) 
504 {
505         GSList *vals;
506         g_return_val_if_fail (req != NULL, NULL);
507         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
508
509         g_warning ("soup_message_get_response_header is DEPRECATED. Use "
510                    "soup_message_get_header, with msg->response_headers as "
511                    "the first argument.\n");
512
513         if (req->response_headers) {
514                 vals = g_hash_table_lookup (req->response_headers, name);
515                 if (vals) 
516                         return vals->data;
517         }
518
519         return NULL;
520 }
521
522 /**
523  * soup_message_queue:
524  * @req: a %SoupMessage.
525  * @callback: a %SoupCallbackFn which will be called after the message completes
526  * or when an unrecoverable error occurs.
527  * @user_data: a pointer passed to @callback.
528  * 
529  * Queues the message @req for sending. All messages are processed while the
530  * glib main loop runs. If this %SoupMessage has been processed before, any
531  * resources related to the time it was last sent are freed.
532  *
533  * If the response %SoupDataBuffer has an owner of %SOUP_BUFFER_USER_OWNED, the
534  * message will not be queued, and @callback will be called with a
535  * %SoupErrorCode of %SOUP_ERROR_CANCELLED.
536  *
537  * Upon message completetion, the callback specified in @callback will be
538  * invoked. If after returning from this callback the message has not been
539  * requeued using %soup_message_queue, %soup_message_free will be called on
540  * @req.
541  */
542 void 
543 soup_message_queue (SoupMessage    *req,
544                     SoupCallbackFn  callback, 
545                     gpointer        user_data)
546 {
547         soup_queue_message (req, callback, user_data);
548 }
549
550 /**
551  * soup_message_send:
552  * @msg: a %SoupMessage.
553  * 
554  * Syncronously send @msg. This call will not return until the transfer is
555  * finished successfully or there is an unrecoverable error. 
556  *
557  * @msg is not free'd upon return.
558  *
559  * Return value: the %SoupErrorClass of the error encountered while sending or
560  * reading the response.
561  */
562 SoupErrorClass
563 soup_message_send (SoupMessage *msg)
564 {
565         soup_message_queue (msg, NULL, NULL);
566
567         while (1) {
568                 g_main_iteration (TRUE); 
569                 if (msg->status == SOUP_STATUS_FINISHED || 
570                     SOUP_ERROR_IS_TRANSPORT (msg->errorcode))
571                         break;
572         }
573
574         return msg->errorclass;
575 }
576
577 static void 
578 authorize_handler (SoupMessage *msg, gboolean proxy)
579 {
580         const GSList *vals;
581         SoupAuth *auth, *old_auth;
582         SoupContext *ctx;
583         const SoupUri *uri;
584
585         ctx = proxy ? soup_get_proxy () : msg->context;
586         uri = soup_context_get_uri (ctx);
587
588         vals = soup_message_get_header_list (msg->response_headers, 
589                                              proxy ? 
590                                                      "Proxy-Authenticate" : 
591                                                      "WWW-Authenticate");
592         if (!vals) goto THROW_CANT_AUTHENTICATE;
593
594         auth = soup_auth_new_from_header_list (vals);
595         if (!auth) {
596                 soup_message_set_error_full (
597                         msg, 
598                         proxy ? 
599                                 SOUP_ERROR_CANT_AUTHENTICATE_PROXY : 
600                                 SOUP_ERROR_CANT_AUTHENTICATE,
601                         proxy ? 
602                                 "Unknown authentication scheme required by "
603                                 "proxy" :
604                                 "Unknown authentication scheme required");
605                 return;
606         }
607
608         /*
609          * Call registered authenticate handler
610          */
611         if (!uri->user && soup_auth_fn)
612                 (*soup_auth_fn) (auth->type,
613                                  (SoupUri *) uri,
614                                  auth->realm, 
615                                  soup_auth_fn_user_data);
616
617         if (!uri->user) {
618                 soup_auth_free (auth);
619                 goto THROW_CANT_AUTHENTICATE;
620         }
621
622         /*
623          * Initialize with auth data (possibly returned from auth callback).
624          */
625         soup_auth_initialize (auth, uri);
626
627         old_auth = soup_auth_lookup (ctx);
628         if (old_auth) {
629                 if (!soup_auth_invalidates_prior (auth, old_auth)) {
630                         soup_auth_free (auth);
631                         goto THROW_CANT_AUTHENTICATE;
632                 }
633         }
634
635         soup_auth_set_context (auth, ctx);
636
637         soup_message_queue (msg, msg->priv->callback, msg->priv->user_data);
638
639         return;
640
641  THROW_CANT_AUTHENTICATE:
642         soup_message_set_error (msg, 
643                                 proxy ? 
644                                         SOUP_ERROR_CANT_AUTHENTICATE_PROXY : 
645                                         SOUP_ERROR_CANT_AUTHENTICATE);
646 }
647
648 static void 
649 redirect_handler (SoupMessage *msg, gpointer user_data)
650 {
651         const gchar *new_loc;
652
653         if (msg->errorclass != SOUP_ERROR_CLASS_REDIRECT || 
654             msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT) return;
655
656         new_loc = soup_message_get_header (msg->response_headers, "Location");
657
658         if (new_loc) {
659                 const SoupUri *old_uri;
660                 SoupUri *new_uri;
661                 SoupContext *new_ctx;
662
663                 old_uri = soup_context_get_uri (msg->context);
664
665                 new_uri = soup_uri_new (new_loc);
666                 if (!new_uri) 
667                         goto INVALID_REDIRECT;
668
669                 /* 
670                  * Copy auth info from original URI.
671                  */
672                 if (old_uri->user && !new_uri->user)
673                         soup_uri_set_auth (new_uri,
674                                            old_uri->user, 
675                                            old_uri->passwd, 
676                                            old_uri->authmech);
677
678                 new_ctx = soup_context_from_uri (new_uri);
679
680                 soup_uri_free (new_uri);
681
682                 if (!new_ctx)
683                         goto INVALID_REDIRECT;
684
685                 soup_message_set_context (msg, new_ctx);
686                 soup_context_unref (new_ctx);
687
688                 soup_message_queue (msg,
689                                     msg->priv->callback, 
690                                     msg->priv->user_data);
691         }
692
693         return;
694
695  INVALID_REDIRECT:
696         soup_message_set_error_full (msg, 
697                                      SOUP_ERROR_MALFORMED,
698                                      "Invalid Redirect URL");
699 }
700
701 typedef enum {
702         RESPONSE_HEADER_HANDLER = 1,
703         RESPONSE_ERROR_CODE_HANDLER,
704         RESPONSE_ERROR_CLASS_HANDLER
705 } SoupHandlerKind;
706
707 typedef struct {
708         SoupHandlerType   type;
709         SoupCallbackFn    handler_cb;
710         gpointer          user_data;
711
712         SoupHandlerKind   kind;
713         union {
714                 guint             errorcode;
715                 SoupErrorClass    errorclass;
716                 const gchar      *header;
717         } data;
718 } SoupHandlerData;
719
720 static SoupHandlerData global_handlers [] = {
721         /* 
722          * Handle redirect response codes 300, 301, 302, 303, and 305.
723          */
724         {
725                 SOUP_HANDLER_PRE_BODY,
726                 redirect_handler, 
727                 NULL, 
728                 RESPONSE_HEADER_HANDLER, 
729                 { (guint) "Location" }
730         },
731         /* 
732          * Handle authorization.
733          */
734         {
735                 SOUP_HANDLER_PRE_BODY,
736                 (SoupCallbackFn) authorize_handler, 
737                 GINT_TO_POINTER (FALSE), 
738                 RESPONSE_ERROR_CODE_HANDLER, 
739                 { 401 }
740         },
741         /* 
742          * Handle proxy authorization.
743          */
744         {
745                 SOUP_HANDLER_PRE_BODY,
746                 (SoupCallbackFn) authorize_handler, 
747                 GINT_TO_POINTER (TRUE), 
748                 RESPONSE_ERROR_CODE_HANDLER, 
749                 { 407 }
750         },
751         { 0 }
752 };
753
754 static inline void 
755 run_handler (SoupMessage     *msg, 
756              SoupHandlerType  invoke_type, 
757              SoupHandlerData *data)
758 {
759         if (data->type != invoke_type) return;
760
761         switch (data->kind) {
762         case RESPONSE_HEADER_HANDLER:
763                 if (!soup_message_get_header (msg->response_headers,
764                                               data->data.header))
765                         return;
766                 break;
767         case RESPONSE_ERROR_CODE_HANDLER:
768                 if (msg->errorcode != data->data.errorcode) return;
769                 break;
770         case RESPONSE_ERROR_CLASS_HANDLER:
771                 if (msg->errorclass != data->data.errorclass) return;
772                 break;
773         default:
774                 break;
775         }
776
777         (*data->handler_cb) (msg, data->user_data);
778 }
779
780 /*
781  * Run each handler with matching criteria (first per-message then global
782  * handlers). If a handler requeues a message, we stop processing and terminate
783  * the current request. 
784  *
785  * After running all handlers, if there is an error set or the invoke type was
786  * post_body, issue the final callback.  
787  *
788  * FIXME: If the errorcode is changed by a handler, we should restart the
789  * processing.  
790  */
791 gboolean
792 soup_message_run_handlers (SoupMessage *msg, SoupHandlerType invoke_type)
793 {
794         GSList *list;
795         SoupHandlerData *data;
796
797         g_return_val_if_fail (msg != NULL, FALSE);
798
799         for (list = msg->priv->content_handlers; list; list = list->next) {
800                 data = list->data;
801
802                 run_handler (msg, invoke_type, data);
803
804                 if (msg->status == SOUP_STATUS_QUEUED) return TRUE;
805         }
806
807         for (data = global_handlers; data->type; data++) {
808                 run_handler (msg, invoke_type, data);
809
810                 if (msg->status == SOUP_STATUS_QUEUED) return TRUE;
811         }
812
813         /*
814          * Issue final callback if the invoke_type is POST_BODY and the error
815          * class is not INFORMATIONAL. 
816          */
817         if (invoke_type == SOUP_HANDLER_POST_BODY && 
818             msg->errorclass != SOUP_ERROR_CLASS_INFORMATIONAL) {
819                 soup_message_issue_callback (msg);
820                 return TRUE;
821         }
822
823         return FALSE;
824 }
825
826 static void 
827 add_handler (SoupMessage      *msg,
828              SoupHandlerType   type,
829              SoupCallbackFn    handler_cb,
830              gpointer          user_data,
831              SoupHandlerKind   kind,
832              const gchar      *header,
833              guint             errorcode,
834              guint             errorclass)
835 {
836         SoupHandlerData *data;
837
838         data = g_new0 (SoupHandlerData, 1);
839         data->type = type;
840         data->handler_cb = handler_cb;
841         data->user_data = user_data;
842         data->kind = kind;
843
844         switch (kind) {
845         case RESPONSE_HEADER_HANDLER:
846                 data->data.header = header;
847                 break;
848         case RESPONSE_ERROR_CODE_HANDLER:
849                 data->data.errorcode = errorcode;
850                 break;
851         case RESPONSE_ERROR_CLASS_HANDLER:
852                 data->data.errorclass = errorclass;
853                 break;
854         default:
855                 break;
856         }
857
858         msg->priv->content_handlers = 
859                 g_slist_append (msg->priv->content_handlers, data);
860 }
861
862 void 
863 soup_message_add_header_handler (SoupMessage      *msg,
864                                  const gchar      *header,
865                                  SoupHandlerType   type,
866                                  SoupCallbackFn    handler_cb,
867                                  gpointer          user_data)
868 {
869         g_return_if_fail (msg != NULL);
870         g_return_if_fail (header != NULL);
871         g_return_if_fail (handler_cb != NULL);
872
873         add_handler (msg, 
874                      type, 
875                      handler_cb, 
876                      user_data, 
877                      RESPONSE_HEADER_HANDLER, 
878                      header, 
879                      0,
880                      0);
881 }
882
883 void 
884 soup_message_add_error_code_handler (SoupMessage      *msg,
885                                      guint             errorcode,
886                                      SoupHandlerType   type,
887                                      SoupCallbackFn    handler_cb,
888                                      gpointer          user_data)
889 {
890         g_return_if_fail (msg != NULL);
891         g_return_if_fail (errorcode != 0);
892         g_return_if_fail (handler_cb != NULL);
893
894         add_handler (msg, 
895                      type, 
896                      handler_cb, 
897                      user_data, 
898                      RESPONSE_ERROR_CODE_HANDLER, 
899                      NULL, 
900                      errorcode,
901                      0);
902 }
903
904 void 
905 soup_message_add_error_class_handler (SoupMessage      *msg,
906                                       SoupErrorClass    errorclass,
907                                       SoupHandlerType   type,
908                                       SoupCallbackFn    handler_cb,
909                                       gpointer          user_data)
910 {
911         g_return_if_fail (msg != NULL);
912         g_return_if_fail (errorclass != 0);
913         g_return_if_fail (handler_cb != NULL);
914
915         add_handler (msg, 
916                      type, 
917                      handler_cb, 
918                      user_data, 
919                      RESPONSE_ERROR_CLASS_HANDLER, 
920                      NULL, 
921                      0,
922                      errorclass);
923 }
924
925 void 
926 soup_message_add_handler (SoupMessage      *msg,
927                           SoupHandlerType   type,
928                           SoupCallbackFn    handler_cb,
929                           gpointer          user_data)
930 {
931         g_return_if_fail (msg != NULL);
932         g_return_if_fail (handler_cb != NULL);
933
934         add_handler (msg, 
935                      type, 
936                      handler_cb, 
937                      user_data, 
938                      0, 
939                      NULL, 
940                      0,
941                      0);
942 }
943
944 void
945 soup_message_remove_handler (SoupMessage     *msg, 
946                              SoupHandlerType  type,
947                              SoupCallbackFn   handler_cb,
948                              gpointer         user_data)
949 {
950         GSList *iter = msg->priv->content_handlers;
951
952         while (iter) {
953                 SoupHandlerData *data = iter->data;
954
955                 if (data->handler_cb == handler_cb &&
956                     data->user_data == user_data &&
957                     data->type == type) {
958                         msg->priv->content_handlers = 
959                                 g_slist_remove_link (
960                                         msg->priv->content_handlers,
961                                         iter);
962                         g_free (data);
963                         break;
964                 }
965                 
966                 iter = iter->next;
967         }
968 }
969
970 static inline gboolean
971 ADDED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
972 {
973         return ((newflags & find) && !(msg->priv->msg_flags & find));
974 }
975
976 static inline gboolean
977 REMOVED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
978 {
979         return (!(newflags & find) && (msg->priv->msg_flags & find));
980 }
981
982 void
983 soup_message_set_flags (SoupMessage *msg, guint flags)
984 {
985         g_return_if_fail (msg != NULL);
986
987         msg->priv->msg_flags = flags;
988 }
989
990 guint
991 soup_message_get_flags (SoupMessage *msg)
992 {
993         g_return_val_if_fail (msg != NULL, 0);
994
995         return msg->priv->msg_flags;
996 }
997
998 void 
999 soup_message_set_http_version  (SoupMessage *msg, SoupHttpVersion version)
1000 {
1001         g_return_if_fail (msg != NULL);
1002
1003         msg->priv->http_version = version;
1004 }
1005
1006 void
1007 soup_message_set_context (SoupMessage       *msg,
1008                           SoupContext       *new_ctx)
1009 {
1010         g_return_if_fail (msg != NULL);
1011         g_return_if_fail (new_ctx != NULL);
1012
1013         soup_context_unref (msg->context);
1014         soup_context_ref (new_ctx);
1015
1016         msg->context = new_ctx;
1017 }
1018
1019 SoupContext *
1020 soup_message_get_context (SoupMessage       *msg)
1021 {
1022         g_return_val_if_fail (msg != NULL, NULL);
1023
1024         soup_context_ref (msg->context);
1025         return msg->context;
1026 }
1027
1028 void
1029 soup_message_set_error (SoupMessage *msg, SoupKnownErrorCode errcode)
1030 {
1031         g_return_if_fail (msg != NULL);
1032         g_return_if_fail (errcode != 0);
1033
1034         g_free ((gchar *) msg->errorphrase);
1035
1036         msg->errorcode = errcode;
1037         msg->errorclass = soup_error_get_class (errcode);
1038         msg->errorphrase = g_strdup (soup_error_get_phrase (errcode));
1039 }
1040
1041 void
1042 soup_message_set_error_full (SoupMessage *msg, 
1043                              guint        errcode, 
1044                              const gchar *errphrase)
1045 {
1046         g_return_if_fail (msg != NULL);
1047         g_return_if_fail (errcode != 0);
1048         g_return_if_fail (errphrase != NULL);
1049
1050         g_free ((gchar *) msg->errorphrase);
1051
1052         msg->errorcode = errcode;
1053         msg->errorclass = soup_error_get_class (errcode);
1054         msg->errorphrase = g_strdup (errphrase);
1055 }
1056
1057 void
1058 soup_message_set_handler_error (SoupMessage *msg, 
1059                                 guint        errcode, 
1060                                 const gchar *errphrase)
1061 {
1062         g_return_if_fail (msg != NULL);
1063         g_return_if_fail (errcode != 0);
1064         g_return_if_fail (errphrase != NULL);
1065
1066         g_free ((gchar *) msg->errorphrase);
1067
1068         msg->errorcode = errcode;
1069         msg->errorclass = SOUP_ERROR_CLASS_HANDLER;
1070         msg->errorphrase = g_strdup (errphrase);
1071 }