Include soup-error.h
[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         if (!uri->user) 
589                 goto THROW_CANT_AUTHENTICATE;
590
591         vals = soup_message_get_header_list (msg->response_headers, 
592                                              proxy ? 
593                                                      "Proxy-Authenticate" : 
594                                                      "WWW-Authenticate");
595         if (!vals) goto THROW_CANT_AUTHENTICATE;
596
597         auth = soup_auth_new_from_header_list (uri, vals);
598         if (!auth) {
599                 soup_message_set_error_full (
600                         msg, 
601                         proxy ? 
602                                 SOUP_ERROR_CANT_AUTHENTICATE_PROXY : 
603                                 SOUP_ERROR_CANT_AUTHENTICATE,
604                         proxy ? 
605                                 "Unknown authentication scheme required by "
606                                 "proxy" :
607                                 "Unknown authentication scheme required");
608                 return;
609         }
610
611         old_auth = soup_auth_lookup (ctx);
612         if (old_auth) {
613                 if (!soup_auth_invalidates_prior (auth, old_auth)) {
614                         soup_auth_free (auth);
615                         goto THROW_CANT_AUTHENTICATE;
616                 }
617         }
618
619         soup_auth_set_context (auth, ctx);
620
621         soup_message_queue (msg, msg->priv->callback, msg->priv->user_data);
622
623         return;
624
625  THROW_CANT_AUTHENTICATE:
626         soup_message_set_error (msg, 
627                                 proxy ? 
628                                         SOUP_ERROR_CANT_AUTHENTICATE_PROXY : 
629                                         SOUP_ERROR_CANT_AUTHENTICATE);
630 }
631
632 static void 
633 redirect_handler (SoupMessage *msg, gpointer user_data)
634 {
635         const gchar *new_loc;
636
637         if (msg->errorclass != SOUP_ERROR_CLASS_REDIRECT || 
638             msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT) return;
639
640         new_loc = soup_message_get_header (msg->response_headers, "Location");
641
642         if (new_loc) {
643                 const SoupUri *old_uri;
644                 SoupUri *new_uri;
645                 SoupContext *new_ctx;
646
647                 old_uri = soup_context_get_uri (msg->context);
648
649                 new_uri = soup_uri_new (new_loc);
650                 if (!new_uri) 
651                         goto INVALID_REDIRECT;
652
653                 /* 
654                  * Copy auth info from original URI.
655                  */
656                 if (old_uri->user && !new_uri->user)
657                         soup_uri_set_auth (new_uri,
658                                            old_uri->user, 
659                                            old_uri->passwd, 
660                                            old_uri->authmech);
661
662                 new_ctx = soup_context_from_uri (new_uri);
663
664                 soup_uri_free (new_uri);
665
666                 if (!new_ctx)
667                         goto INVALID_REDIRECT;
668
669                 soup_message_set_context (msg, new_ctx);
670                 soup_context_unref (new_ctx);
671
672                 soup_message_queue (msg,
673                                     msg->priv->callback, 
674                                     msg->priv->user_data);
675         }
676
677         return;
678
679  INVALID_REDIRECT:
680         soup_message_set_error_full (msg, 
681                                      SOUP_ERROR_MALFORMED,
682                                      "Invalid Redirect URL");
683 }
684
685 typedef enum {
686         RESPONSE_HEADER_HANDLER = 1,
687         RESPONSE_ERROR_CODE_HANDLER,
688         RESPONSE_ERROR_CLASS_HANDLER
689 } SoupHandlerKind;
690
691 typedef struct {
692         SoupHandlerType   type;
693         SoupCallbackFn    handler_cb;
694         gpointer          user_data;
695
696         SoupHandlerKind   kind;
697         union {
698                 guint             errorcode;
699                 SoupErrorClass    errorclass;
700                 const gchar      *header;
701         } data;
702 } SoupHandlerData;
703
704 static SoupHandlerData global_handlers [] = {
705         /* 
706          * Handle redirect response codes 300, 301, 302, 303, and 305.
707          */
708         {
709                 SOUP_HANDLER_PRE_BODY,
710                 redirect_handler, 
711                 NULL, 
712                 RESPONSE_HEADER_HANDLER, 
713                 { (guint) "Location" }
714         },
715         /* 
716          * Handle authorization.
717          */
718         {
719                 SOUP_HANDLER_PRE_BODY,
720                 (SoupCallbackFn) authorize_handler, 
721                 GINT_TO_POINTER (FALSE), 
722                 RESPONSE_ERROR_CODE_HANDLER, 
723                 { 401 }
724         },
725         /* 
726          * Handle proxy authorization.
727          */
728         {
729                 SOUP_HANDLER_PRE_BODY,
730                 (SoupCallbackFn) authorize_handler, 
731                 GINT_TO_POINTER (TRUE), 
732                 RESPONSE_ERROR_CODE_HANDLER, 
733                 { 407 }
734         },
735         { 0 }
736 };
737
738 static inline void 
739 run_handler (SoupMessage     *msg, 
740              SoupHandlerType  invoke_type, 
741              SoupHandlerData *data)
742 {
743         if (data->type != invoke_type) return;
744
745         switch (data->kind) {
746         case RESPONSE_HEADER_HANDLER:
747                 if (!soup_message_get_header (msg->response_headers,
748                                               data->data.header))
749                         return;
750                 break;
751         case RESPONSE_ERROR_CODE_HANDLER:
752                 if (msg->errorcode != data->data.errorcode) return;
753                 break;
754         case RESPONSE_ERROR_CLASS_HANDLER:
755                 if (msg->errorclass != data->data.errorclass) return;
756                 break;
757         default:
758                 break;
759         }
760
761         (*data->handler_cb) (msg, data->user_data);
762 }
763
764 /*
765  * Run each handler with matching criteria (first per-message then global
766  * handlers). If a handler requeues a message, we stop processing and terminate
767  * the current request. 
768  *
769  * After running all handlers, if there is an error set or the invoke type was
770  * post_body, issue the final callback.  
771  *
772  * FIXME: If the errorcode is changed by a handler, we should restart the
773  * processing.  
774  */
775 gboolean
776 soup_message_run_handlers (SoupMessage *msg, SoupHandlerType invoke_type)
777 {
778         GSList *list;
779         SoupHandlerData *data;
780
781         g_return_val_if_fail (msg != NULL, FALSE);
782
783         for (list = msg->priv->content_handlers; list; list = list->next) {
784                 data = list->data;
785
786                 run_handler (msg, invoke_type, data);
787
788                 if (msg->status == SOUP_STATUS_QUEUED) return TRUE;
789         }
790
791         for (data = global_handlers; data->type; data++) {
792                 run_handler (msg, invoke_type, data);
793
794                 if (msg->status == SOUP_STATUS_QUEUED) return TRUE;
795         }
796
797         /*
798          * Issue final callback if the invoke_type is POST_BODY and the error
799          * class is not INFORMATIONAL. 
800          */
801         if (invoke_type == SOUP_HANDLER_POST_BODY && 
802             msg->errorclass != SOUP_ERROR_CLASS_INFORMATIONAL) {
803                 soup_message_issue_callback (msg);
804                 return TRUE;
805         }
806
807         return FALSE;
808 }
809
810 static void 
811 add_handler (SoupMessage      *msg,
812              SoupHandlerType   type,
813              SoupCallbackFn    handler_cb,
814              gpointer          user_data,
815              SoupHandlerKind   kind,
816              const gchar      *header,
817              guint             errorcode,
818              guint             errorclass)
819 {
820         SoupHandlerData *data;
821
822         data = g_new0 (SoupHandlerData, 1);
823         data->type = type;
824         data->handler_cb = handler_cb;
825         data->user_data = user_data;
826         data->kind = kind;
827
828         switch (kind) {
829         case RESPONSE_HEADER_HANDLER:
830                 data->data.header = header;
831                 break;
832         case RESPONSE_ERROR_CODE_HANDLER:
833                 data->data.errorcode = errorcode;
834                 break;
835         case RESPONSE_ERROR_CLASS_HANDLER:
836                 data->data.errorclass = errorclass;
837                 break;
838         default:
839                 break;
840         }
841
842         msg->priv->content_handlers = 
843                 g_slist_append (msg->priv->content_handlers, data);
844 }
845
846 void 
847 soup_message_add_header_handler (SoupMessage      *msg,
848                                  const gchar      *header,
849                                  SoupHandlerType   type,
850                                  SoupCallbackFn    handler_cb,
851                                  gpointer          user_data)
852 {
853         g_return_if_fail (msg != NULL);
854         g_return_if_fail (header != NULL);
855         g_return_if_fail (handler_cb != NULL);
856
857         add_handler (msg, 
858                      type, 
859                      handler_cb, 
860                      user_data, 
861                      RESPONSE_HEADER_HANDLER, 
862                      header, 
863                      0,
864                      0);
865 }
866
867 void 
868 soup_message_add_error_code_handler (SoupMessage      *msg,
869                                      guint             errorcode,
870                                      SoupHandlerType   type,
871                                      SoupCallbackFn    handler_cb,
872                                      gpointer          user_data)
873 {
874         g_return_if_fail (msg != NULL);
875         g_return_if_fail (errorcode != 0);
876         g_return_if_fail (handler_cb != NULL);
877
878         add_handler (msg, 
879                      type, 
880                      handler_cb, 
881                      user_data, 
882                      RESPONSE_ERROR_CODE_HANDLER, 
883                      NULL, 
884                      errorcode,
885                      0);
886 }
887
888 void 
889 soup_message_add_error_class_handler (SoupMessage      *msg,
890                                       SoupErrorClass    errorclass,
891                                       SoupHandlerType   type,
892                                       SoupCallbackFn    handler_cb,
893                                       gpointer          user_data)
894 {
895         g_return_if_fail (msg != NULL);
896         g_return_if_fail (errorclass != 0);
897         g_return_if_fail (handler_cb != NULL);
898
899         add_handler (msg, 
900                      type, 
901                      handler_cb, 
902                      user_data, 
903                      RESPONSE_ERROR_CLASS_HANDLER, 
904                      NULL, 
905                      0,
906                      errorclass);
907 }
908
909 void 
910 soup_message_add_handler (SoupMessage      *msg,
911                           SoupHandlerType   type,
912                           SoupCallbackFn    handler_cb,
913                           gpointer          user_data)
914 {
915         g_return_if_fail (msg != NULL);
916         g_return_if_fail (handler_cb != NULL);
917
918         add_handler (msg, 
919                      type, 
920                      handler_cb, 
921                      user_data, 
922                      0, 
923                      NULL, 
924                      0,
925                      0);
926 }
927
928 void
929 soup_message_remove_handler (SoupMessage     *msg, 
930                              SoupHandlerType  type,
931                              SoupCallbackFn   handler_cb,
932                              gpointer         user_data)
933 {
934         GSList *iter = msg->priv->content_handlers;
935
936         while (iter) {
937                 SoupHandlerData *data = iter->data;
938
939                 if (data->handler_cb == handler_cb &&
940                     data->user_data == user_data &&
941                     data->type == type) {
942                         msg->priv->content_handlers = 
943                                 g_slist_remove_link (
944                                         msg->priv->content_handlers,
945                                         iter);
946                         g_free (data);
947                         break;
948                 }
949                 
950                 iter = iter->next;
951         }
952 }
953
954 static inline gboolean
955 ADDED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
956 {
957         return ((newflags & find) && !(msg->priv->msg_flags & find));
958 }
959
960 static inline gboolean
961 REMOVED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
962 {
963         return (!(newflags & find) && (msg->priv->msg_flags & find));
964 }
965
966 void
967 soup_message_set_flags (SoupMessage *msg, guint flags)
968 {
969         g_return_if_fail (msg != NULL);
970
971         msg->priv->msg_flags = flags;
972 }
973
974 guint
975 soup_message_get_flags (SoupMessage *msg)
976 {
977         g_return_val_if_fail (msg != NULL, 0);
978
979         return msg->priv->msg_flags;
980 }
981
982 void 
983 soup_message_set_http_version  (SoupMessage *msg, SoupHttpVersion version)
984 {
985         g_return_if_fail (msg != NULL);
986
987         msg->priv->http_version = version;
988 }
989
990 void
991 soup_message_set_context (SoupMessage       *msg,
992                           SoupContext       *new_ctx)
993 {
994         soup_context_unref (msg->context);
995         msg->context = new_ctx;
996         soup_context_ref (new_ctx);
997 }
998
999 SoupContext *
1000 soup_message_get_context (SoupMessage       *msg)
1001 {
1002         soup_context_ref (msg->context);
1003         return msg->context;
1004 }
1005
1006 void
1007 soup_message_set_error (SoupMessage *msg, SoupKnownErrorCode errcode)
1008 {
1009         g_return_if_fail (msg != NULL);
1010         g_return_if_fail (errcode != 0);
1011
1012         g_free ((gchar *) msg->errorphrase);
1013
1014         msg->errorcode = errcode;
1015         msg->errorclass = soup_error_get_class (errcode);
1016         msg->errorphrase = g_strdup (soup_error_get_phrase (errcode));
1017 }
1018
1019 void
1020 soup_message_set_error_full (SoupMessage *msg, 
1021                              guint        errcode, 
1022                              const gchar *errphrase)
1023 {
1024         g_return_if_fail (msg != NULL);
1025         g_return_if_fail (errcode != 0);
1026         g_return_if_fail (errphrase != NULL);
1027
1028         g_free ((gchar *) msg->errorphrase);
1029
1030         msg->errorcode = errcode;
1031         msg->errorclass = soup_error_get_class (errcode);
1032         msg->errorphrase = g_strdup (errphrase);
1033 }
1034
1035 void
1036 soup_message_set_handler_error (SoupMessage *msg, 
1037                                 guint        errcode, 
1038                                 const gchar *errphrase)
1039 {
1040         g_return_if_fail (msg != NULL);
1041         g_return_if_fail (errcode != 0);
1042         g_return_if_fail (errphrase != NULL);
1043
1044         g_free ((gchar *) msg->errorphrase);
1045
1046         msg->errorcode = errcode;
1047         msg->errorclass = SOUP_ERROR_CLASS_HANDLER;
1048         msg->errorphrase = g_strdup (errphrase);
1049 }