Build fixes for Windows.
[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-message.h"
13 #include "soup-misc.h"
14 #include "soup-context.h"
15 #include "soup-private.h"
16 #include "soup-queue.h"
17 #include "soup-transfer.h"
18
19 /**
20  * soup_message_new:
21  * @context: a %SoupContext for the destination endpoint.
22  * @method: a string which will be used as the HTTP method for the created
23  * request.
24  * 
25  * Creates a new empty %SoupMessage, which will connect to the URL represented
26  * by @context. The new message has a status of @SOUP_STATUS_IDLE.
27  *
28  * Return value: the new %SoupMessage.
29  */
30 SoupMessage *
31 soup_message_new (SoupContext *context, const gchar *method) 
32 {
33         SoupMessage *ret;
34
35         g_return_val_if_fail (context, NULL);
36
37         ret          = g_new0 (SoupMessage, 1);
38         ret->priv    = g_new0 (SoupMessagePrivate, 1);
39         ret->status  = SOUP_STATUS_IDLE;
40         ret->context = context;
41         ret->method  = method ? method : SOUP_METHOD_POST;
42
43         ret->priv->http_version = SOUP_HTTP_1_1;
44
45         soup_context_ref (context);
46
47         return ret;
48 }
49
50 /**
51  * soup_message_new_full:
52  * @context: a %SoupContext for the destination endpoint.
53  * @method: a string which will be used as the HTTP method for the created
54  * request.
55  * @req_owner: the %SoupOwnership of the passed data buffer.
56  * @req_body: a data buffer containing the body of the message request.
57  * @req_length: the byte length of @req_body.
58  * 
59  * Creates a new %SoupMessage, which will connect to the URL represented by
60  * @context. The new message has a status of @SOUP_STATUS_IDLE. The request data
61  * buffer will be filled from @req_owner, @req_body, and @req_length
62  * respectively.
63  *
64  * Return value: the new %SoupMessage.
65  */
66 SoupMessage *
67 soup_message_new_full (SoupContext   *context,
68                        const gchar   *method,
69                        SoupOwnership  req_owner,
70                        gchar         *req_body,
71                        gulong         req_length)
72 {
73         SoupMessage *ret = soup_message_new (context, method);
74
75         ret->request.owner = req_owner;
76         ret->request.body = req_body;
77         ret->request.length = req_length;
78
79         return ret;
80 }
81
82 /**
83  * soup_message_cleanup:
84  * @req: a %SoupMessage.
85  * 
86  * Frees any temporary resources created in the processing of @req. Request and
87  * response data buffers are left intact.
88  */
89 void 
90 soup_message_cleanup (SoupMessage *req)
91 {
92         g_return_if_fail (req != NULL);
93
94         if (req->priv->read_tag) {
95                 soup_transfer_read_cancel (req->priv->read_tag);
96                 req->priv->read_tag = 0;
97         }
98
99         if (req->priv->write_tag) {
100                 soup_transfer_write_cancel (req->priv->write_tag);
101                 req->priv->write_tag = 0;
102         }
103
104         if (req->priv->connect_tag) {
105                 soup_context_cancel_connect (req->priv->connect_tag);
106                 req->priv->connect_tag = NULL;
107         }
108         if (req->connection) {
109                 soup_connection_release (req->connection);
110                 req->connection = NULL;
111         }
112
113         soup_active_requests = g_slist_remove (soup_active_requests, req);
114 }
115
116 static void
117 free_header (gchar *name, gchar *value, gpointer unused)
118 {
119         g_free (name);
120         g_free (value);
121 }
122
123 static void
124 finalize_message (SoupMessage *req)
125 {
126         soup_context_unref (req->context);
127
128         if (req->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
129                 g_free (req->request.body);
130         if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
131                 g_free (req->response.body);
132
133         if (req->priv->req_header) 
134                 g_string_free (req->priv->req_header, TRUE);
135
136         if (req->request_headers) {
137                 g_hash_table_foreach (req->request_headers,
138                                       (GHFunc) free_header,
139                                       NULL);
140                 g_hash_table_destroy (req->request_headers);
141         }
142
143         if (req->response_headers) {
144                 g_hash_table_foreach (req->response_headers,
145                                       (GHFunc) free_header,
146                                       NULL);
147                 g_hash_table_destroy (req->response_headers);
148         }
149
150         g_slist_foreach (req->priv->content_handlers, (GFunc) g_free, NULL);
151         g_slist_free (req->priv->content_handlers);
152
153         g_free ((gchar *) req->errorphrase);
154         g_free (req->priv);
155         g_free (req);
156 }
157
158 /**
159  * soup_message_free:
160  * @req: a %SoupMessage to destroy.
161  * 
162  * Destroys the %SoupMessage pointed to by @req. Request and response headers
163  * are freed. Request and response data buffers are also freed if their
164  * ownership is %SOUP_BUFFER_SYSTEM_OWNED. The message's destination context
165  * will be de-referenced.
166  */
167 void 
168 soup_message_free (SoupMessage *req)
169 {
170         g_return_if_fail (req != NULL);
171
172         soup_message_cleanup (req);
173
174         finalize_message (req);
175 }
176
177 /**
178  * soup_message_issue_callback:
179  * @req: a %SoupMessage currently being processed.
180  * @error: a %SoupErrorCode to be passed to %req's completion callback.
181  * 
182  * Finalizes the message request, by first freeing any temporary resources, then
183  * issuing the callback function pointer passed in %soup_message_new or
184  * %soup_message_new_full. If, after returning from the callback, the message
185  * has not been requeued, @msg is destroyed using %soup_message_free.
186  */
187 void
188 soup_message_issue_callback (SoupMessage *req)
189 {
190         g_return_if_fail (req != NULL);
191
192         /* 
193          * Make sure we don't have some icky recursion if the callback 
194          * runs the main loop, and the connection has some data or error 
195          * which causes the callback to be run again.
196          */
197         soup_message_cleanup (req);
198
199         if (req->priv->callback) {
200                 (*req->priv->callback) (req, req->priv->user_data);
201
202                 if (req->status != SOUP_STATUS_QUEUED)
203                         finalize_message (req);
204         }
205 }
206
207 /**
208  * soup_message_cancel:
209  * @req: a %SoupMessage currently being processed.
210  * 
211  * Cancel a running message, and issue completion callback with a
212  * %SoupTransferStatus of %SOUP_ERROR_CANCELLED. If not requeued by the
213  * completion callback, the @msg will be destroyed.
214  */
215 void 
216 soup_message_cancel (SoupMessage *msg) 
217 {
218         soup_message_set_error (msg, SOUP_ERROR_CANCELLED);
219         soup_message_issue_callback (msg);
220 }
221
222 static void 
223 soup_message_set_header (GHashTable  **hash,
224                          const gchar  *name,
225                          const gchar  *value) 
226 {
227         gpointer old_name, old_value;
228
229         if (!*hash) 
230                 *hash = g_hash_table_new (soup_str_case_hash, 
231                                           soup_str_case_equal);
232         else if (g_hash_table_lookup_extended (*hash, 
233                                                name, 
234                                                &old_name, 
235                                                &old_value)) {
236                 g_hash_table_remove (*hash, name);
237                 g_free (old_name);
238                 g_free (old_value);
239         }
240
241         if (value)
242                 g_hash_table_insert (*hash, g_strdup (name), g_strdup (value));
243 }
244
245 /**
246  * soup_message_set_request_header:
247  * @req: a %SoupMessage.
248  * @name: header name.
249  * @value: header value.
250  * 
251  * Adds a new transport header to be sent on an outgoing request. Passing a NULL
252  * @value will remove the header name supplied.
253  */
254 void
255 soup_message_set_request_header (SoupMessage *req,
256                                  const gchar *name,
257                                  const gchar *value) 
258 {
259         g_return_if_fail (req != NULL);
260         g_return_if_fail (name != NULL || name [0] != '\0');
261
262         soup_message_set_header (&req->request_headers, name, value);
263 }
264
265 /**
266  * soup_message_get_request_header:
267  * @req: a %SoupMessage.
268  * @name: header name.
269  * 
270  * Lookup the transport request header with a key equal to @name.
271  *
272  * Return value: the header's value or NULL if not found.
273  */
274 const gchar *
275 soup_message_get_request_header (SoupMessage *req,
276                                  const gchar *name) 
277 {
278         g_return_val_if_fail (req != NULL, NULL);
279         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
280
281         return req->request_headers ? 
282                 g_hash_table_lookup (req->request_headers, name) : NULL;
283 }
284
285 /**
286  * soup_message_set_response_header:
287  * @req: a %SoupMessage.
288  * @name: header name.
289  * @value: header value.
290  * 
291  * Adds a new transport header to be sent on an outgoing response. Passing a
292  * NULL @value will remove the header name supplied.
293  */
294 void
295 soup_message_set_response_header (SoupMessage *req,
296                                   const gchar *name,
297                                   const gchar *value) 
298 {
299         g_return_if_fail (req != NULL);
300         g_return_if_fail (name != NULL || name [0] != '\0');
301
302         soup_message_set_header (&req->response_headers, name, value);
303 }
304
305 /**
306  * soup_message_get_response_header:
307  * @req: a %SoupMessage.
308  * @name: header name.
309  * 
310  * Lookup the transport response header with a key equal to @name.
311  *
312  * Return value: the header's value or NULL if not found.
313  */
314 const gchar *
315 soup_message_get_response_header (SoupMessage *req,
316                                   const gchar *name) 
317 {
318         g_return_val_if_fail (req != NULL, NULL);
319         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
320
321         return req->response_headers ? 
322                 g_hash_table_lookup (req->response_headers, name) : NULL;
323 }
324
325 /**
326  * soup_message_queue:
327  * @req: a %SoupMessage.
328  * @callback: a %SoupCallbackFn which will be called after the message completes
329  * or when an unrecoverable error occurs.
330  * @user_data: a pointer passed to @callback.
331  * 
332  * Queues the message @req for sending. All messages are processed while the
333  * glib main loop runs. If this %SoupMessage has been processed before, any
334  * resources related to the time it was last sent are freed.
335  *
336  * If the response %SoupDataBuffer has an owner of %SOUP_BUFFER_USER_OWNED, the
337  * message will not be queued, and @callback will be called with a
338  * %SoupErrorCode of %SOUP_ERROR_CANCELLED.
339  *
340  * Upon message completetion, the callback specified in @callback will be
341  * invoked. If after returning from this callback the message has not been
342  * requeued using %soup_message_queue, %soup_message_free will be called on
343  * @req.
344  */
345 void 
346 soup_message_queue (SoupMessage    *req,
347                     SoupCallbackFn  callback, 
348                     gpointer        user_data)
349 {
350         soup_queue_message (req, callback, user_data);
351 }
352
353 /**
354  * soup_message_send:
355  * @msg: a %SoupMessage.
356  * 
357  * Syncronously send @msg. This call will not return until the transfer is
358  * finished successfully or there is an unrecoverable error. 
359  *
360  * @msg is not free'd upon return.
361  *
362  * Return value: the %SoupErrorClass of the error encountered while sending or
363  * reading the response.
364  */
365 SoupErrorClass
366 soup_message_send (SoupMessage *msg)
367 {
368         soup_message_queue (msg, NULL, NULL);
369
370         while (1) {
371                 g_main_iteration (TRUE); 
372                 if (msg->status == SOUP_STATUS_FINISHED || 
373                     msg->errorcode != 0)
374                         break;
375         }
376
377         return msg->errorclass;
378 }
379
380 static void 
381 authorize_handler (SoupMessage *msg, gboolean proxy)
382 {
383         const char *auth_header;
384         SoupAuth *auth;
385         SoupContext *ctx;
386
387         ctx = proxy ? soup_get_proxy () : msg->context;
388
389         if (!soup_context_get_uri (ctx)->user) 
390                 goto THROW_CANT_AUTHENTICATE;
391
392         auth_header = 
393                 soup_message_get_response_header (
394                         msg, 
395                         proxy ? "Proxy-Authenticate" : "WWW-Authenticate");
396         if (!auth_header) goto THROW_CANT_AUTHENTICATE;
397
398         auth = soup_auth_new_from_header (ctx, auth_header);
399         if (!auth) {
400                 soup_message_set_error_full (
401                         msg, 
402                         proxy ? 
403                                 SOUP_ERROR_CANT_AUTHENTICATE_PROXY : 
404                                 SOUP_ERROR_CANT_AUTHENTICATE,
405                         proxy ? 
406                                 "Unknown authentication scheme "
407                                 "required by proxy" :
408                                 "Unknown authentication scheme "
409                                 "required");
410                 return;
411         }
412
413         if (ctx->auth) {
414                 if (soup_auth_invalidates_prior (auth, ctx->auth))
415                         soup_auth_free (ctx->auth);
416                 else {
417                         soup_auth_free (auth);
418                         goto THROW_CANT_AUTHENTICATE;
419                 }
420         }
421
422         ctx->auth = auth;
423
424         soup_message_queue (msg, msg->priv->callback, msg->priv->user_data);
425
426         return;
427
428  THROW_CANT_AUTHENTICATE:
429         soup_message_set_error (msg, 
430                                 proxy ? 
431                                         SOUP_ERROR_CANT_AUTHENTICATE_PROXY : 
432                                         SOUP_ERROR_CANT_AUTHENTICATE);
433 }
434
435 static void 
436 redirect_handler (SoupMessage *msg, gpointer user_data)
437 {
438         const gchar *new_url;
439
440         if (msg->errorclass != SOUP_ERROR_CLASS_REDIRECT || 
441             msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT) return;
442
443         new_url = soup_message_get_response_header (msg, "Location");
444
445         if (new_url) {
446                 SoupContext *new_ctx, *old_ctx;
447
448                 new_ctx = soup_context_get (new_url);
449                 if (!new_ctx) {
450                         soup_message_set_error_full (msg, 
451                                                      SOUP_ERROR_MALFORMED,
452                                                      "Invalid Redirect URL");
453                         return;
454                 }
455
456                 old_ctx = msg->context;
457                 msg->context = new_ctx;
458
459                 soup_message_queue (msg,
460                                     msg->priv->callback, 
461                                     msg->priv->user_data);
462
463                 soup_context_unref (old_ctx);
464         }
465 }
466
467 typedef enum {
468         RESPONSE_HEADER_HANDLER = 1,
469         RESPONSE_ERROR_CODE_HANDLER,
470         RESPONSE_ERROR_CLASS_HANDLER
471 } SoupHandlerKind;
472
473 typedef struct {
474         SoupHandlerType   type;
475         SoupCallbackFn    handler_cb;
476         gpointer          user_data;
477
478         SoupHandlerKind   kind;
479         union {
480                 guint             errorcode;\r
481                 SoupErrorClass    errorclass;\r
482                 const gchar      *header;
483         } data;
484 } SoupHandlerData;
485
486 static SoupHandlerData global_handlers [] = {
487         /* 
488          * Handle authorization.
489          */
490         {
491                 SOUP_HANDLER_PRE_BODY,
492                 (SoupCallbackFn) authorize_handler, 
493                 GINT_TO_POINTER (FALSE), 
494                 RESPONSE_ERROR_CODE_HANDLER, 
495                 { 401 }
496         },
497         /* 
498          * Handle proxy authorization.
499          */
500         {
501                 SOUP_HANDLER_PRE_BODY,
502                 (SoupCallbackFn) authorize_handler, 
503                 GINT_TO_POINTER (TRUE), 
504                 RESPONSE_ERROR_CODE_HANDLER, 
505                 { 407 }
506         },
507         /* 
508          * Handle redirect response codes 300, 301, 302, 303, and 305.
509          */
510         {
511                 SOUP_HANDLER_PRE_BODY,
512                 redirect_handler, 
513                 NULL, 
514                 RESPONSE_HEADER_HANDLER, 
515                 { (guint) "Location" }
516         },
517         { 0 }
518 };
519
520 static inline void 
521 run_handler (SoupMessage     *msg, 
522              SoupHandlerType  invoke_type, 
523              SoupHandlerData *data)
524 {
525         if (data->type != invoke_type) return;
526
527         switch (data->kind) {
528         case RESPONSE_HEADER_HANDLER:
529                 if (!soup_message_get_response_header (msg,
530                                                        data->data.header))
531                         return;
532                 break;
533         case RESPONSE_ERROR_CODE_HANDLER:
534                 if (msg->errorcode != data->data.errorcode) return;
535                 break;
536         case RESPONSE_ERROR_CLASS_HANDLER:
537                 if (msg->errorclass != data->data.errorclass) return;
538                 break;
539         default:
540                 break;
541         }
542
543         (*data->handler_cb) (msg, data->user_data);
544 }
545
546 /*
547  * Run each handler with matching criteria (first per-message then global
548  * handlers). If a handler requeues a message, we stop processing and terminate
549  * the current request. 
550  *
551  * After running all handlers, if there is an error set or the invoke type was
552  * post_body, issue the final callback.  
553  *
554  * FIXME: If the errorcode is changed by a handler, we should restart the
555  * processing.  
556  */
557 gboolean
558 soup_message_run_handlers (SoupMessage *msg, SoupHandlerType invoke_type)
559 {
560         GSList *list;
561         SoupHandlerData *data;
562
563         g_return_val_if_fail (msg != NULL, FALSE);
564
565         for (list = msg->priv->content_handlers; list; list = list->next) {
566                 data = list->data;
567
568                 run_handler (msg, invoke_type, data);
569
570                 if (msg->status == SOUP_STATUS_QUEUED) return TRUE;
571         }
572
573         for (data = global_handlers; data->type; data++) {
574                 run_handler (msg, invoke_type, data);
575
576                 if (msg->status == SOUP_STATUS_QUEUED) return TRUE;
577         }
578
579         /*
580          * Issue final callback if the invoke_type is POST_BODY and the error
581          * class is not INFORMATIONAL. 
582          */
583         if (invoke_type == SOUP_HANDLER_POST_BODY && 
584             msg->errorclass != SOUP_ERROR_CLASS_INFORMATIONAL) {
585                 soup_message_issue_callback (msg);
586                 return TRUE;
587         }
588
589         return FALSE;
590 }
591
592 static void 
593 add_handler (SoupMessage      *msg,
594              SoupHandlerType   type,
595              SoupCallbackFn    handler_cb,
596              gpointer          user_data,
597              SoupHandlerKind   kind,
598              const gchar      *header,
599              guint             errorcode,
600              guint             errorclass)
601 {
602         SoupHandlerData *data;
603
604         data = g_new0 (SoupHandlerData, 1);
605         data->type = type;
606         data->handler_cb = handler_cb;
607         data->user_data = user_data;
608         data->kind = kind;
609
610         switch (kind) {
611         case RESPONSE_HEADER_HANDLER:
612                 data->data.header = header;
613                 break;
614         case RESPONSE_ERROR_CODE_HANDLER:
615                 data->data.errorcode = errorcode;
616                 break;
617         case RESPONSE_ERROR_CLASS_HANDLER:
618                 data->data.errorclass = errorclass;
619                 break;
620         default:
621                 break;
622         }
623
624         msg->priv->content_handlers = 
625                 g_slist_append (msg->priv->content_handlers, data);
626 }
627
628 void 
629 soup_message_add_header_handler (SoupMessage      *msg,
630                                  const gchar      *header,
631                                  SoupHandlerType   type,
632                                  SoupCallbackFn    handler_cb,
633                                  gpointer          user_data)
634 {
635         g_return_if_fail (msg != NULL);
636         g_return_if_fail (header != NULL);
637         g_return_if_fail (handler_cb != NULL);
638
639         add_handler (msg, 
640                      type, 
641                      handler_cb, 
642                      user_data, 
643                      RESPONSE_HEADER_HANDLER, 
644                      header, 
645                      0,
646                      0);
647 }
648
649 void 
650 soup_message_add_error_code_handler (SoupMessage      *msg,
651                                      guint             errorcode,
652                                      SoupHandlerType   type,
653                                      SoupCallbackFn    handler_cb,
654                                      gpointer          user_data)
655 {
656         g_return_if_fail (msg != NULL);
657         g_return_if_fail (errorcode != 0);
658         g_return_if_fail (handler_cb != NULL);
659
660         add_handler (msg, 
661                      type, 
662                      handler_cb, 
663                      user_data, 
664                      RESPONSE_ERROR_CODE_HANDLER, 
665                      NULL, 
666                      errorcode,
667                      0);
668 }
669
670 void 
671 soup_message_add_error_class_handler (SoupMessage      *msg,
672                                       SoupErrorClass    errorclass,
673                                       SoupHandlerType   type,
674                                       SoupCallbackFn    handler_cb,
675                                       gpointer          user_data)
676 {
677         g_return_if_fail (msg != NULL);
678         g_return_if_fail (errorclass != 0);
679         g_return_if_fail (handler_cb != NULL);
680
681         add_handler (msg, 
682                      type, 
683                      handler_cb, 
684                      user_data, 
685                      RESPONSE_ERROR_CLASS_HANDLER, 
686                      NULL, 
687                      0,
688                      errorclass);
689 }
690
691 void 
692 soup_message_add_handler (SoupMessage      *msg,
693                           SoupHandlerType   type,
694                           SoupCallbackFn    handler_cb,
695                           gpointer          user_data)
696 {
697         g_return_if_fail (msg != NULL);
698         g_return_if_fail (handler_cb != NULL);
699
700         add_handler (msg, 
701                      type, 
702                      handler_cb, 
703                      user_data, 
704                      0, 
705                      NULL, 
706                      0,
707                      0);
708 }
709
710 void
711 soup_message_remove_handler (SoupMessage     *msg, 
712                              SoupHandlerType  type,
713                              SoupCallbackFn   handler_cb,
714                              gpointer         user_data)
715 {
716         GSList *iter = msg->priv->content_handlers;
717
718         while (iter) {
719                 SoupHandlerData *data = iter->data;
720
721                 if (data->handler_cb == handler_cb &&
722                     data->user_data == user_data &&
723                     data->type == type) {
724                         msg->priv->content_handlers = 
725                                 g_slist_remove_link (
726                                         msg->priv->content_handlers,
727                                         iter);
728                         g_free (data);
729                         break;
730                 }
731                 
732                 iter = iter->next;
733         }
734 }
735
736 static inline gboolean
737 ADDED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
738 {
739         return ((newflags & find) && !(msg->priv->msg_flags & find));
740 }
741
742 static inline gboolean
743 REMOVED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
744 {
745         return (!(newflags & find) && (msg->priv->msg_flags & find));
746 }
747
748 void
749 soup_message_set_flags (SoupMessage *msg, guint flags)
750 {
751         g_return_if_fail (msg != NULL);
752
753         msg->priv->msg_flags = flags;
754 }
755
756 guint
757 soup_message_get_flags (SoupMessage *msg)
758 {
759         g_return_val_if_fail (msg != NULL, 0);
760
761         return msg->priv->msg_flags;
762 }
763
764 void 
765 soup_message_set_http_version  (SoupMessage *msg, SoupHttpVersion version)
766 {
767         g_return_if_fail (msg != NULL);
768
769         msg->priv->http_version = version;
770 }
771
772 void
773 soup_message_set_error (SoupMessage *msg, SoupKnownErrorCode errcode)
774 {
775         g_return_if_fail (msg != NULL);
776         g_return_if_fail (errcode != 0);
777
778         g_free ((gchar *) msg->errorphrase);
779
780         msg->errorcode = errcode;
781         msg->errorclass = soup_get_error_class (errcode);
782         msg->errorphrase = g_strdup (soup_get_error_phrase (errcode));
783 }
784
785 void
786 soup_message_set_error_full (SoupMessage *msg, 
787                              guint        errcode, 
788                              const gchar *errphrase)
789 {
790         g_return_if_fail (msg != NULL);
791         g_return_if_fail (errcode != 0);
792         g_return_if_fail (errphrase != NULL);
793
794         g_free ((gchar *) msg->errorphrase);
795
796         msg->errorcode = errcode;
797         msg->errorclass = soup_get_error_class (errcode);
798         msg->errorphrase = g_strdup (errphrase);
799 }
800
801 void
802 soup_message_set_handler_error (SoupMessage *msg, 
803                                 guint        errcode, 
804                                 const gchar *errphrase)
805 {
806         g_return_if_fail (msg != NULL);
807         g_return_if_fail (errcode != 0);
808         g_return_if_fail (errphrase != NULL);
809
810         g_free ((gchar *) msg->errorphrase);
811
812         msg->errorcode = errcode;
813         msg->errorclass = SOUP_ERROR_CLASS_HANDLER;
814         msg->errorphrase = g_strdup (errphrase);
815 }
816
817 struct {
818         guint sc;
819         const gchar *phrase;
820 } error_code_phrases [] = {
821         /* 
822          * SOUP_ERROR_CLASS_TRANSPORT 
823          */
824         { SOUP_ERROR_CANCELLED,               "Cancelled" },
825         { SOUP_ERROR_CANT_CONNECT,            "Cannot connect to destination" },
826         { SOUP_ERROR_CANT_CONNECT_PROXY,      "Cannot connect to proxy" },
827         { SOUP_ERROR_IO,                      "Connection terminated "
828                                               "unexpectadly" },
829         { SOUP_ERROR_MALFORMED,               "Message Corrupt" },
830         { SOUP_ERROR_CANT_AUTHENTICATE,       "Authentication Failed" },
831         { SOUP_ERROR_CANT_AUTHENTICATE_PROXY, "Proxy Authentication Failed" },
832
833         /* 
834          * SOUP_ERROR_CLASS_INFORMATIONAL 
835          */
836         { SOUP_ERROR_CONTINUE,        "Continue" },
837         { SOUP_ERROR_PROTOCOL_SWITCH, "Protocol Switch" },
838         { SOUP_ERROR_DAV_PROCESSING,  "Processing" },
839
840         /* 
841          * SOUP_ERROR_CLASS_SUCCESS 
842          */
843         { SOUP_ERROR_OK,                "OK" },
844         { SOUP_ERROR_CREATED,           "Created" },
845         { SOUP_ERROR_ACCEPTED,          "Accepted" },
846         { SOUP_ERROR_NON_AUTHORITATIVE, "Non-Authoritative" },
847         { SOUP_ERROR_NO_CONTENT,        "No Content" },
848         { SOUP_ERROR_RESET_CONTENT,     "Reset Content" },
849         { SOUP_ERROR_PARTIAL_CONTENT,   "Partial Content" },
850         { SOUP_ERROR_DAV_MULTISTATUS,   "Multi-Status" },
851
852         /* 
853          * SOUP_ERROR_CLASS_REDIRECT 
854          */
855         { SOUP_ERROR_MULTIPLE_CHOICES,   "Multiple Choices" },
856         { SOUP_ERROR_MOVED_PERMANANTLY,  "Moved Permanantly" },
857         { SOUP_ERROR_FOUND,              "Found" },
858         { SOUP_ERROR_SEE_OTHER,          "See Other" },
859         { SOUP_ERROR_NOT_MODIFIED,       "Not Modified" },
860         { SOUP_ERROR_USE_PROXY,          "Use Proxy" },
861         { SOUP_ERROR_TEMPORARY_REDIRECT, "Temporary Redirect" },
862
863         /* 
864          * SOUP_ERROR_CLASS_CLIENT_ERROR 
865          */
866         { SOUP_ERROR_BAD_REQUEST,           "Bad Request" },
867         { SOUP_ERROR_UNAUTHORIZED,          "Unauthorized" },
868         { SOUP_ERROR_PAYMENT_REQUIRED,      "Payment Required" },
869         { SOUP_ERROR_FORBIDDEN,             "Forbidden" },
870         { SOUP_ERROR_NOT_FOUND,             "Not Found" },
871         { SOUP_ERROR_METHOD_NOT_ALLOWED,    "Method Not Allowed" },
872         { SOUP_ERROR_NOT_ACCEPTABLE,        "Not Acceptable" },
873         { SOUP_ERROR_PROXY_UNAUTHORIZED,    "Proxy Unauthorized" },
874         { SOUP_ERROR_TIMED_OUT,             "Timed Out" },
875         { SOUP_ERROR_CONFLICT,              "Conflict" },
876         { SOUP_ERROR_GONE,                  "Gone" },
877         { SOUP_ERROR_LENGTH_REQUIRED,       "Length Required" },
878         { SOUP_ERROR_PRECONDITION_FAILED,   "Precondition Failed" },
879         { SOUP_ERROR_BODY_TOO_LARGE,        "Entity Body Too Large" },
880         { SOUP_ERROR_URI_TOO_LARGE,         "Request-URI Too Large" },
881         { SOUP_ERROR_UNKNOWN_MEDIA_TYPE,    "Unknown Media Type" },
882         { SOUP_ERROR_INVALID_RANGE,         "Invalid Range" },
883         { SOUP_ERROR_EXPECTATION_FAILED,    "Expectation Failed" },
884         { SOUP_ERROR_DAV_UNPROCESSABLE,     "Unprocessable Entity" },
885         { SOUP_ERROR_DAV_LOCKED,            "Locked" },
886         { SOUP_ERROR_DAV_DEPENDENCY_FAILED, "Dependency Failed" },
887
888         /* 
889          * SOUP_ERROR_CLASS_SERVER_ERROR 
890          */
891         { SOUP_ERROR_INTERNAL,            "Internal Server Error" },
892         { SOUP_ERROR_NOT_IMPLEMENTED,     "Not Implemented" },
893         { SOUP_ERROR_BAD_GATEWAY,         "Bad Gateway" },
894         { SOUP_ERROR_SERVICE_UNAVAILABLE, "Service Unavailable" },
895         { SOUP_ERROR_GATEWAY_TIMEOUT,     "Gateway Timeout" },
896         { SOUP_ERROR_VERSION_UNSUPPORTED, "Version Unsupported" },
897         { SOUP_ERROR_DAV_OUT_OF_SPACE,    "Out Of Space" },
898
899         { 0 }
900 };
901
902 const gchar *
903 soup_get_error_phrase (SoupKnownErrorCode errcode)
904 {
905         gint i;
906
907         for (i = 0; error_code_phrases [i].sc; i++) {
908                 if (error_code_phrases [i].sc == (guint) errcode)
909                         return error_code_phrases [i].phrase;
910         }
911
912         return "Unknown Error";
913 }
914
915 SoupErrorClass
916 soup_get_error_class (SoupKnownErrorCode errcode)
917 {
918         if (errcode < 100) return SOUP_ERROR_CLASS_TRANSPORT;
919         if (errcode < 200) return SOUP_ERROR_CLASS_INFORMATIONAL;
920         if (errcode < 300) return SOUP_ERROR_CLASS_SUCCESS;
921         if (errcode < 400) return SOUP_ERROR_CLASS_REDIRECT;
922         if (errcode < 500) return SOUP_ERROR_CLASS_CLIENT_ERROR;
923         if (errcode < 600) return SOUP_ERROR_CLASS_SERVER_ERROR;
924         return SOUP_ERROR_CLASS_UNKNOWN;
925 }