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