Add querysting to hash. (soup_context_uri_equal): Compare querystring for
[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-message.h"
12 #include "soup-misc.h"
13 #include "soup-context.h"
14 #include "soup-private.h"
15 #include "soup-transfer.h"
16
17 static SoupErrorCode 
18 authorize_handler (SoupMessage *msg, gboolean proxy)
19 {
20         const char *auth_header;
21         SoupAuth *auth;
22         SoupContext *ctx;
23
24         ctx = proxy ? soup_get_proxy () : msg->context;
25
26         auth_header = 
27                 soup_message_get_response_header (
28                         msg, 
29                         proxy ? "Proxy-Authenticate" : "WWW-Authenticate");
30         if (!auth_header) return SOUP_ERROR_CANT_AUTHENTICATE;
31
32         auth = soup_auth_new_from_header (ctx, auth_header);
33         if (!auth) return SOUP_ERROR_MALFORMED_HEADER;
34
35         if (ctx->auth) {
36                 if (soup_auth_invalidates_prior (auth))
37                         soup_auth_free (ctx->auth);
38                 else {
39                         soup_auth_free (auth);
40                         return SOUP_ERROR_CANT_AUTHENTICATE;
41                 }
42         }
43
44         ctx->auth = auth;
45
46         if (msg->priv->req_header) {
47                 g_string_free (msg->priv->req_header, TRUE);
48                 msg->priv->req_header = NULL;
49         }
50
51         soup_message_queue (msg, msg->priv->callback, msg->priv->user_data);
52
53         return SOUP_ERROR_NONE;
54 }
55
56 static SoupErrorCode 
57 redirect_handler (SoupMessage *msg, gpointer user_data)
58 {
59         const gchar *new_url;
60
61         switch (msg->response_code) {
62         case 300: /* Multiple Choices */
63         case 301: /* Moved Permanently */
64         case 302: /* Moved Temporarily */
65         case 303: /* See Other */
66         case 305: /* Use Proxy */
67                 break;
68         default:
69                 return SOUP_ERROR_NONE;
70         }
71
72         if (msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT) 
73                 return SOUP_ERROR_NONE;
74
75         new_url = soup_message_get_response_header (msg, "Location");
76
77         if (new_url) {
78                 SoupContext *new_ctx = soup_context_get (new_url);
79                 if (!new_ctx) return SOUP_ERROR_MALFORMED_HEADER;
80
81                 soup_context_unref (msg->context);
82                 msg->context = new_ctx;
83
84                 soup_message_queue (msg,
85                                     msg->priv->callback, 
86                                     msg->priv->user_data);
87         }
88
89         return SOUP_ERROR_NONE;
90 }
91
92 /**
93  * soup_message_new:
94  * @context: a %SoupContext for the destination endpoint.
95  * @action: a string which will be used as the SOAPAction header for the created
96  * request.
97  * 
98  * Creates a new empty %SoupMessage, which will connect to the URL represented
99  * by @context. The new message has a status of @SOUP_STATUS_IDLE.
100  *
101  * Return value: the new %SoupMessage.
102  */
103 SoupMessage *
104 soup_message_new (SoupContext *context, SoupAction action) 
105 {
106         SoupMessage *ret;
107
108         g_return_val_if_fail (context, NULL);
109
110         ret          = g_new0 (SoupMessage, 1);
111         ret->priv    = g_new0 (SoupMessagePrivate, 1);
112         ret->status  = SOUP_STATUS_IDLE;
113         ret->action  = g_strdup (action);
114         ret->context = context;
115         ret->method  = SOUP_METHOD_POST;
116
117         soup_context_ref (context);
118
119         /*
120          * Add a 401 (Authorization Required) response code handler if the
121          * context URI has a login user name.
122          */
123         if (soup_context_get_uri (context)->user)
124                 soup_message_add_response_code_handler (
125                         ret, 
126                         401, 
127                         SOUP_HANDLER_POST_BODY, 
128                         (SoupHandlerFn) authorize_handler, 
129                         GINT_TO_POINTER (FALSE));
130
131         /*
132          * Always add a 407 (Proxy-Authorization Required) handler, in case the
133          * proxy is reset after message creation.
134          */
135         soup_message_add_response_code_handler (
136                         ret, 
137                         407, 
138                         SOUP_HANDLER_POST_BODY, 
139                         (SoupHandlerFn) authorize_handler, 
140                         GINT_TO_POINTER (TRUE));
141
142         /* 
143          * Handle redirect response codes 300, 301, 302, 303, and 305.
144          */
145         soup_message_add_header_handler (ret,
146                                          "Location",
147                                          SOUP_HANDLER_PRE_BODY,
148                                          redirect_handler,
149                                          NULL);
150
151         return ret;
152 }
153
154 /**
155  * soup_message_new_full:
156  * @context: a %SoupContext for the destination endpoint.
157  * @action: a string which will be used as the SOAPAction header for the created
158  * request.
159  * @req_owner: the %SoupOwnership of the passed data buffer.
160  * @req_body: a data buffer containing the body of the message request.
161  * @req_length: the byte length of @req_body.
162  * 
163  * Creates a new %SoupMessage, which will connect to the URL represented by
164  * @context. The new message has a status of @SOUP_STATUS_IDLE. The request data
165  * buffer will be filled from @req_owner, @req_body, and @req_length
166  * respectively.
167  *
168  * Return value: the new %SoupMessage.
169  */
170 SoupMessage *
171 soup_message_new_full (SoupContext   *context,
172                        SoupAction     action,
173                        SoupOwnership  req_owner,
174                        gchar         *req_body,
175                        gulong         req_length)
176 {
177         SoupMessage *ret = soup_message_new (context, action);
178
179         ret->request.owner = req_owner;
180         ret->request.body = req_body;
181         ret->request.length = req_length;
182
183         return ret;
184 }
185
186 /**
187  * soup_message_cleanup:
188  * @req: a %SoupMessage.
189  * @action: a string which will be used as the SOAPAction header for the created
190  * request.
191  * 
192  * Frees any temporary resources created in the processing of @req. Request and
193  * response data buffers are left intact.
194  */
195 void 
196 soup_message_cleanup (SoupMessage *req)
197 {
198         g_return_if_fail (req != NULL);
199
200         if (req->priv->read_tag) {
201                 soup_transfer_read_cancel (req->priv->read_tag);
202                 req->priv->read_tag = 0;
203         }
204
205         if (req->priv->write_tag) {
206                 soup_transfer_write_cancel (req->priv->write_tag);
207                 req->priv->write_tag = 0;
208         }
209
210         if (req->priv->connect_tag) {
211                 soup_context_cancel_connect (req->priv->connect_tag);
212                 req->priv->connect_tag = NULL;
213         }
214         if (req->priv->conn) {
215                 soup_connection_release (req->priv->conn);
216                 req->priv->conn = NULL;
217         }
218
219         soup_active_requests = g_slist_remove (soup_active_requests, req);
220 }
221
222 static void
223 soup_message_remove_header (gchar *name, gchar *value, gpointer unused)
224 {
225         g_free (name);
226         g_free (value);
227 }
228
229 /**
230  * soup_message_free:
231  * @req: a %SoupMessage to destroy.
232  * 
233  * Destroys the %SoupMessage pointed to by @req. Request and response headers
234  * are freed. Request and response data buffers are also freed if their
235  * ownership is %SOUP_BUFFER_SYSTEM_OWNED. The message's destination context
236  * will be de-referenced.
237  */
238 void 
239 soup_message_free (SoupMessage *req)
240 {
241         g_return_if_fail (req != NULL);
242
243         soup_message_cleanup (req);
244
245         soup_context_unref (req->context);
246
247         if (req->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
248                 g_free (req->request.body);
249         if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
250                 g_free (req->response.body);
251
252         if (req->priv->req_header) 
253                 g_string_free (req->priv->req_header, TRUE);
254
255         if (req->request_headers) {
256                 g_hash_table_foreach (req->request_headers,
257                                       (GHFunc) soup_message_remove_header,
258                                       NULL);
259                 g_hash_table_destroy (req->request_headers);
260         }
261
262         if (req->response_headers) {
263                 g_hash_table_foreach (req->response_headers,
264                                       (GHFunc) soup_message_remove_header,
265                                       NULL);
266                 g_hash_table_destroy (req->response_headers);
267         }
268
269         g_slist_foreach (req->priv->content_handlers, (GFunc) g_free, NULL);
270         g_slist_free (req->priv->content_handlers);
271
272         g_free (req->priv);
273         g_free (req->action);
274         g_free (req);
275 }
276
277 /**
278  * soup_message_issue_callback:
279  * @req: a %SoupMessage currently being processed.
280  * @error: a %SoupErrorCode to be passed to %req's completion callback.
281  * 
282  * Finalizes the message request, by first freeing any temporary resources, then
283  * issuing the callback function pointer passed in %soup_message_new or
284  * %soup_message_new_full. If, after returning from the callback, the message
285  * has not been requeued, @msg is destroyed using %soup_message_free.
286  */
287 void
288 soup_message_issue_callback (SoupMessage *req, SoupErrorCode error)
289 {
290         g_return_if_fail (req != NULL);
291
292         /* 
293          * Make sure we don't have some icky recursion if the callback 
294          * runs the main loop, and the connection has some data or error 
295          * which causes the callback to be run again.
296          */
297         soup_message_cleanup (req);
298
299         req->priv->errorcode = error;
300
301         if (req->priv->callback) {
302                 (*req->priv->callback) (req, error, req->priv->user_data);
303
304                 if (req->status != SOUP_STATUS_QUEUED)
305                         soup_message_free (req);
306         }
307 }
308
309 /**
310  * soup_message_cancel:
311  * @req: a %SoupMessage currently being processed.
312  * 
313  * Cancel a running message, and issue completion callback with a
314  * %SoupTransferStatus of %SOUP_ERROR_CANCELLED. If not requeued by the
315  * completion callback, the @msg will be destroyed.
316  */
317 void 
318 soup_message_cancel (SoupMessage *req) 
319 {
320         soup_message_issue_callback (req, SOUP_ERROR_CANCELLED);
321 }
322
323 static void 
324 soup_message_set_header (GHashTable  **hash,
325                          const gchar  *name,
326                          const gchar  *value) 
327 {
328         gpointer old_name, old_value;
329
330         if (!*hash) 
331                 *hash = g_hash_table_new (soup_str_case_hash, 
332                                           soup_str_case_equal);
333         else if (g_hash_table_lookup_extended (*hash, 
334                                                name, 
335                                                &old_name, 
336                                                &old_value)) {
337                 g_hash_table_remove (*hash, name);
338                 g_free (old_name);
339                 g_free (old_value);
340         }
341
342         if (value)
343                 g_hash_table_insert (*hash, g_strdup (name), g_strdup (value));
344 }
345
346 /**
347  * soup_message_set_request_header:
348  * @req: a %SoupMessage.
349  * @name: header name.
350  * @value: header value.
351  * 
352  * Adds a new transport header to be sent on an outgoing request. Passing a NULL
353  * @value will remove the header name supplied.
354  */
355 void
356 soup_message_set_request_header (SoupMessage *req,
357                                  const gchar *name,
358                                  const gchar *value) 
359 {
360         g_return_if_fail (req != NULL);
361         g_return_if_fail (name != NULL || name [0] != '\0');
362
363         if (req->priv->req_header) {
364                 g_string_free (req->priv->req_header, TRUE);
365                 req->priv->req_header = NULL;
366         }
367
368         soup_message_set_header (&req->request_headers, name, value);
369 }
370
371 /**
372  * soup_message_get_request_header:
373  * @req: a %SoupMessage.
374  * @name: header name.
375  * 
376  * Lookup the transport request header with a key equal to @name.
377  *
378  * Return value: the header's value or NULL if not found.
379  */
380 const gchar *
381 soup_message_get_request_header (SoupMessage *req,
382                                  const gchar *name) 
383 {
384         g_return_val_if_fail (req != NULL, NULL);
385         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
386
387         return req->request_headers ? 
388                 g_hash_table_lookup (req->request_headers, name) : NULL;
389 }
390
391 /**
392  * soup_message_set_response_header:
393  * @req: a %SoupMessage.
394  * @name: header name.
395  * @value: header value.
396  * 
397  * Adds a new transport header to be sent on an outgoing response. Passing a
398  * NULL @value will remove the header name supplied.
399  */
400 void
401 soup_message_set_response_header (SoupMessage *req,
402                                   const gchar *name,
403                                   const gchar *value) 
404 {
405         g_return_if_fail (req != NULL);
406         g_return_if_fail (name != NULL || name [0] != '\0');
407
408         soup_message_set_header (&req->response_headers, name, value);
409 }
410
411 /**
412  * soup_message_get_response_header:
413  * @req: a %SoupMessage.
414  * @name: header name.
415  * 
416  * Lookup the transport response header with a key equal to @name.
417  *
418  * Return value: the header's value or NULL if not found.
419  */
420 const gchar *
421 soup_message_get_response_header (SoupMessage *req,
422                                   const gchar *name) 
423 {
424         g_return_val_if_fail (req != NULL, NULL);
425         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
426
427         return req->response_headers ? 
428                 g_hash_table_lookup (req->response_headers, name) : NULL;
429 }
430
431 /**
432  * soup_message_send:
433  * @msg: a %SoupMessage.
434  * 
435  * Syncronously send @msg. This call will not return until the transfer is
436  * finished successfully or there is an unrecoverable error. 
437  *
438  * @msg is not free'd upon return.
439  *
440  * Return value: the %SoupErrorCode of the error encountered while sending, or
441  * SOUP_ERROR_NONE.
442  */
443 SoupErrorCode 
444 soup_message_send (SoupMessage *msg)
445 {
446         soup_message_queue (msg, NULL, NULL);
447
448         while (1) {
449                 g_main_iteration (TRUE); 
450                 if (msg->status == SOUP_STATUS_FINISHED ||
451                     msg->priv->errorcode != SOUP_ERROR_NONE)
452                         return msg->priv->errorcode;
453         }
454
455         return SOUP_ERROR_NONE;
456 }
457
458 void
459 soup_message_set_method (SoupMessage *msg, const gchar *method)
460 {
461         g_return_if_fail (msg != NULL);
462         g_return_if_fail (method != NULL);
463
464         msg->method = method;
465 }
466
467 const gchar *
468 soup_message_get_method (SoupMessage *msg)
469 {
470         g_return_val_if_fail (msg != NULL, NULL);
471
472         return msg->method;
473 }
474
475 typedef enum {
476         RESPONSE_HEADER_HANDLER,
477         RESPONSE_CODE_HANDLER,
478         RESPONSE_BODY_HANDLER
479 } SoupHandlerKind;
480
481 typedef struct {
482         SoupHandlerType   type;
483         SoupHandlerFn     handler_cb;
484         gpointer          user_data;
485
486         SoupHandlerKind   kind;
487         const gchar      *header;
488         guint             code;
489 } SoupHandlerData;
490
491 static void 
492 soup_message_add_handler (SoupMessage      *msg,
493                           SoupHandlerType   type,
494                           SoupHandlerFn     handler_cb,
495                           gpointer          user_data,
496                           SoupHandlerKind   kind,
497                           const gchar      *header,
498                           guint             code)
499 {
500         SoupHandlerData *data;
501
502         data = g_new0 (SoupHandlerData, 1);
503         data->type = type;
504         data->handler_cb = handler_cb;
505         data->user_data = user_data;
506         data->kind = kind;
507         data->header = header;
508         data->code = code;
509
510         msg->priv->content_handlers = 
511                 g_slist_append (msg->priv->content_handlers, data);
512 }
513
514 void 
515 soup_message_add_header_handler (SoupMessage      *msg,
516                                  const gchar      *header,
517                                  SoupHandlerType   type,
518                                  SoupHandlerFn     handler_cb,
519                                  gpointer          user_data)
520 {
521         g_return_if_fail (msg != NULL);
522         g_return_if_fail (header != NULL);
523         g_return_if_fail (handler_cb != NULL);
524
525         soup_message_add_handler (msg, 
526                                   type, 
527                                   handler_cb, 
528                                   user_data, 
529                                   RESPONSE_HEADER_HANDLER, 
530                                   header, 
531                                   0);
532 }
533
534 void 
535 soup_message_add_response_code_handler (SoupMessage      *msg,
536                                         guint             code,
537                                         SoupHandlerType   type,
538                                         SoupHandlerFn     handler_cb,
539                                         gpointer          user_data)
540 {
541         g_return_if_fail (msg != NULL);
542         g_return_if_fail (code != 0);
543         g_return_if_fail (handler_cb != NULL);
544
545         soup_message_add_handler (msg, 
546                                   type, 
547                                   handler_cb, 
548                                   user_data, 
549                                   RESPONSE_CODE_HANDLER, 
550                                   NULL, 
551                                   code);
552 }
553
554 void 
555 soup_message_add_body_handler (SoupMessage      *msg,
556                                SoupHandlerType   type,
557                                SoupHandlerFn     handler_cb,
558                                gpointer          user_data)
559 {
560         g_return_if_fail (msg != NULL);
561         g_return_if_fail (handler_cb != NULL);
562
563         soup_message_add_handler (msg, 
564                                   type, 
565                                   handler_cb, 
566                                   user_data, 
567                                   RESPONSE_BODY_HANDLER, 
568                                   NULL, 
569                                   0);
570 }
571
572 SoupErrorCode 
573 soup_message_run_handlers (SoupMessage *msg, SoupHandlerType invoke_type)
574 {
575         GSList *list;
576         SoupErrorCode retval = SOUP_ERROR_NONE;
577
578         g_return_val_if_fail (msg != NULL, retval);
579         
580         for (list = msg->priv->content_handlers; list; list = list->next) {
581                 SoupHandlerData *data = list->data;
582                 
583                 if (data->type != invoke_type) continue;
584
585                 switch (data->kind) {
586                 case RESPONSE_HEADER_HANDLER:
587                         if (!soup_message_get_response_header (msg,
588                                                                data->header))
589                                 continue;
590                         break;
591                 case RESPONSE_CODE_HANDLER:
592                         if (msg->response_code != data->code) continue;
593                         break;
594                 case RESPONSE_BODY_HANDLER:
595                         break;
596                 }
597
598                 retval = (*data->handler_cb) (msg, data->user_data);
599
600                 if (retval != SOUP_ERROR_NONE) break;
601                 if (msg->status == SOUP_STATUS_QUEUED) break;
602         }
603
604         return retval;
605 }
606
607 static void
608 soup_message_remove_handler (SoupMessage   *msg, 
609                              SoupHandlerFn  handler_cb,
610                              gpointer       user_data)
611 {
612         GSList *iter = msg->priv->content_handlers;
613
614         while (iter) {
615                 SoupHandlerData *data = iter->data;
616
617                 if (data->handler_cb == handler_cb &&
618                     data->user_data == user_data) {
619                         msg->priv->content_handlers = 
620                                 g_slist_remove_link (
621                                         msg->priv->content_handlers,
622                                         iter);
623                         g_free (data);
624                         break;
625                 }
626                 
627                 iter = iter->next;
628         }
629 }
630
631 static inline gboolean
632 ADDED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
633 {
634         return ((newflags & find) && !(msg->priv->msg_flags & find));
635 }
636
637 static inline gboolean
638 REMOVED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
639 {
640         return (!(newflags & find) && (msg->priv->msg_flags & find));
641 }
642
643 void
644 soup_message_set_flags (SoupMessage *msg, guint flags)
645 {
646         g_return_if_fail (msg != NULL);
647
648         if (ADDED_FLAG (msg, flags, SOUP_MESSAGE_NO_REDIRECT))
649                 soup_message_remove_handler (msg, 
650                                              redirect_handler,
651                                              NULL);
652         else if (REMOVED_FLAG (msg, flags, SOUP_MESSAGE_NO_REDIRECT))
653                 soup_message_add_header_handler (msg,
654                                                  "Location",
655                                                  SOUP_HANDLER_PRE_BODY,
656                                                  redirect_handler,
657                                                  NULL);
658
659         msg->priv->msg_flags = flags;
660 }
661
662 guint
663 soup_message_get_flags (SoupMessage *msg)
664 {
665         return msg->priv->msg_flags;
666 }