Only set up a tunnel if the destination protocol is HTTPS.
[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: HTTP request/response
4  *
5  * Copyright (C) 2000-2003, Ximian, Inc.
6  */
7
8 #include <string.h>
9
10 #include "soup-auth.h"
11 #include "soup-marshal.h"
12 #include "soup-message.h"
13 #include "soup-message-private.h"
14 #include "soup-misc.h"
15 #include "soup-uri.h"
16
17 #define PARENT_TYPE G_TYPE_OBJECT
18 static GObjectClass *parent_class;
19
20 enum {
21         WROTE_INFORMATIONAL,
22         WROTE_HEADERS,
23         WROTE_CHUNK,
24         WROTE_BODY,
25
26         GOT_INFORMATIONAL,
27         GOT_HEADERS,
28         GOT_CHUNK,
29         GOT_BODY,
30
31         RESTARTED,
32         FINISHED,
33
34         LAST_SIGNAL
35 };
36
37 static guint signals[LAST_SIGNAL] = { 0 };
38
39 static void wrote_body (SoupMessage *req);
40 static void got_headers (SoupMessage *req);
41 static void got_chunk (SoupMessage *req);
42 static void got_body (SoupMessage *req);
43 static void stop_io (SoupMessage *req);
44 static void free_chunks (SoupMessage *msg);
45
46 static void
47 init (GObject *object)
48 {
49         SoupMessage *msg = SOUP_MESSAGE (object);
50
51         msg->priv = g_new0 (SoupMessagePrivate, 1);
52
53         msg->status  = SOUP_MESSAGE_STATUS_IDLE;
54
55         msg->request_headers = g_hash_table_new (soup_str_case_hash,
56                                                  soup_str_case_equal);
57
58         msg->response_headers = g_hash_table_new (soup_str_case_hash,
59                                                   soup_str_case_equal);
60
61         msg->priv->http_version = SOUP_HTTP_1_1;
62 }
63
64 static void
65 finalize (GObject *object)
66 {
67         SoupMessage *msg = SOUP_MESSAGE (object);
68
69         soup_message_io_cancel (msg);
70
71         if (msg->priv->uri)
72                 soup_uri_free (msg->priv->uri);
73
74         if (msg->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
75                 g_free (msg->request.body);
76         if (msg->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
77                 g_free (msg->response.body);
78         free_chunks (msg);
79
80         soup_message_clear_headers (msg->request_headers);
81         g_hash_table_destroy (msg->request_headers);
82
83         soup_message_clear_headers (msg->response_headers);
84         g_hash_table_destroy (msg->response_headers);
85
86         g_slist_foreach (msg->priv->content_handlers, (GFunc) g_free, NULL);
87         g_slist_free (msg->priv->content_handlers);
88
89         g_free ((char *) msg->reason_phrase);
90
91         g_free (msg->priv);
92
93         G_OBJECT_CLASS (parent_class)->finalize (object);
94 }
95
96 static void
97 class_init (GObjectClass *object_class)
98 {
99         SoupMessageClass *message_class = SOUP_MESSAGE_CLASS (object_class);
100
101         parent_class = g_type_class_ref (PARENT_TYPE);
102
103         /* virtual method definition */
104         message_class->wrote_body   = wrote_body;
105         message_class->got_headers  = got_headers;
106         message_class->got_chunk    = got_chunk;
107         message_class->got_body     = got_body;
108         message_class->restarted    = stop_io;
109         message_class->finished     = stop_io;
110
111         /* virtual method override */
112         object_class->finalize = finalize;
113
114         /* signals */
115         signals[WROTE_INFORMATIONAL] =
116                 g_signal_new ("wrote_informational",
117                               G_OBJECT_CLASS_TYPE (object_class),
118                               G_SIGNAL_RUN_FIRST,
119                               G_STRUCT_OFFSET (SoupMessageClass, wrote_informational),
120                               NULL, NULL,
121                               soup_marshal_NONE__NONE,
122                               G_TYPE_NONE, 0);
123         signals[WROTE_HEADERS] =
124                 g_signal_new ("wrote_headers",
125                               G_OBJECT_CLASS_TYPE (object_class),
126                               G_SIGNAL_RUN_FIRST,
127                               G_STRUCT_OFFSET (SoupMessageClass, wrote_headers),
128                               NULL, NULL,
129                               soup_marshal_NONE__NONE,
130                               G_TYPE_NONE, 0);
131         signals[WROTE_CHUNK] =
132                 g_signal_new ("wrote_chunk",
133                               G_OBJECT_CLASS_TYPE (object_class),
134                               G_SIGNAL_RUN_FIRST,
135                               G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk),
136                               NULL, NULL,
137                               soup_marshal_NONE__NONE,
138                               G_TYPE_NONE, 0);
139         signals[WROTE_BODY] =
140                 g_signal_new ("wrote_body",
141                               G_OBJECT_CLASS_TYPE (object_class),
142                               G_SIGNAL_RUN_FIRST,
143                               G_STRUCT_OFFSET (SoupMessageClass, wrote_body),
144                               NULL, NULL,
145                               soup_marshal_NONE__NONE,
146                               G_TYPE_NONE, 0);
147
148         signals[GOT_INFORMATIONAL] =
149                 g_signal_new ("got_informational",
150                               G_OBJECT_CLASS_TYPE (object_class),
151                               G_SIGNAL_RUN_FIRST,
152                               G_STRUCT_OFFSET (SoupMessageClass, got_informational),
153                               NULL, NULL,
154                               soup_marshal_NONE__NONE,
155                               G_TYPE_NONE, 0);
156         signals[GOT_HEADERS] =
157                 g_signal_new ("got_headers",
158                               G_OBJECT_CLASS_TYPE (object_class),
159                               G_SIGNAL_RUN_FIRST,
160                               G_STRUCT_OFFSET (SoupMessageClass, got_headers),
161                               NULL, NULL,
162                               soup_marshal_NONE__NONE,
163                               G_TYPE_NONE, 0);
164         signals[GOT_CHUNK] =
165                 g_signal_new ("got_chunk",
166                               G_OBJECT_CLASS_TYPE (object_class),
167                               G_SIGNAL_RUN_FIRST,
168                               G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
169                               NULL, NULL,
170                               soup_marshal_NONE__NONE,
171                               G_TYPE_NONE, 0);
172         signals[GOT_BODY] =
173                 g_signal_new ("got_body",
174                               G_OBJECT_CLASS_TYPE (object_class),
175                               G_SIGNAL_RUN_FIRST,
176                               G_STRUCT_OFFSET (SoupMessageClass, got_body),
177                               NULL, NULL,
178                               soup_marshal_NONE__NONE,
179                               G_TYPE_NONE, 0);
180
181         signals[RESTARTED] =
182                 g_signal_new ("restarted",
183                               G_OBJECT_CLASS_TYPE (object_class),
184                               G_SIGNAL_RUN_FIRST,
185                               G_STRUCT_OFFSET (SoupMessageClass, restarted),
186                               NULL, NULL,
187                               soup_marshal_NONE__NONE,
188                               G_TYPE_NONE, 0);
189         signals[FINISHED] =
190                 g_signal_new ("finished",
191                               G_OBJECT_CLASS_TYPE (object_class),
192                               G_SIGNAL_RUN_FIRST,
193                               G_STRUCT_OFFSET (SoupMessageClass, finished),
194                               NULL, NULL,
195                               soup_marshal_NONE__NONE,
196                               G_TYPE_NONE, 0);
197 }
198
199 SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE)
200
201
202 /**
203  * soup_message_new:
204  * @method: the HTTP method for the created request
205  * @uri_string: the destination endpoint (as a string)
206  * 
207  * Creates a new empty #SoupMessage, which will connect to @uri
208  *
209  * Return value: the new #SoupMessage (or %NULL if @uri could not
210  * be parsed).
211  */
212 SoupMessage *
213 soup_message_new (const char *method, const char *uri_string)
214 {
215         SoupMessage *msg;
216         SoupUri *uri;
217
218         uri = soup_uri_new (uri_string);
219         if (!uri)
220                 return NULL;
221
222         msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
223         msg->method = method ? method : SOUP_METHOD_GET;
224         msg->priv->uri = uri;
225
226         return msg;
227 }
228
229 /**
230  * soup_message_new_from_uri:
231  * @method: the HTTP method for the created request
232  * @uri: the destination endpoint (as a #SoupUri)
233  * 
234  * Creates a new empty #SoupMessage, which will connect to @uri
235  *
236  * Return value: the new #SoupMessage
237  */
238 SoupMessage *
239 soup_message_new_from_uri (const char *method, const SoupUri *uri)
240 {
241         SoupMessage *msg;
242
243         msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
244         msg->method = method ? method : SOUP_METHOD_GET;
245         msg->priv->uri = soup_uri_copy (uri);
246
247         return msg;
248 }
249
250 /**
251  * soup_message_set_request:
252  * @msg: the message
253  * @content_type: MIME Content-Type of the body
254  * @req_owner: the #SoupOwnership of the passed data buffer.
255  * @req_body: a data buffer containing the body of the message request.
256  * @req_length: the byte length of @req_body.
257  * 
258  * Convenience function to set the request body of a #SoupMessage
259  */
260 void
261 soup_message_set_request (SoupMessage   *msg,
262                           const char    *content_type,
263                           SoupOwnership  req_owner,
264                           char          *req_body,
265                           gulong         req_length)
266 {
267         g_return_if_fail (SOUP_IS_MESSAGE (msg));
268         g_return_if_fail (content_type != NULL);
269         g_return_if_fail (req_body != NULL || req_length == 0);
270
271         soup_message_add_header (msg->request_headers,
272                                  "Content-Type", content_type);
273         msg->request.owner = req_owner;
274         msg->request.body = req_body;
275         msg->request.length = req_length;
276 }
277
278 /**
279  * soup_message_set_response:
280  * @msg: the message
281  * @content_type: MIME Content-Type of the body
282  * @resp_owner: the #SoupOwnership of the passed data buffer.
283  * @resp_body: a data buffer containing the body of the message response.
284  * @resp_length: the byte length of @resp_body.
285  * 
286  * Convenience function to set the response body of a #SoupMessage
287  */
288 void
289 soup_message_set_response (SoupMessage   *msg,
290                            const char    *content_type,
291                            SoupOwnership  resp_owner,
292                            char          *resp_body,
293                            gulong         resp_length)
294 {
295         g_return_if_fail (SOUP_IS_MESSAGE (msg));
296         g_return_if_fail (content_type != NULL);
297         g_return_if_fail (resp_body != NULL || resp_length == 0);
298
299         soup_message_add_header (msg->response_headers,
300                                  "Content-Type", content_type);
301         msg->response.owner = resp_owner;
302         msg->response.body = resp_body;
303         msg->response.length = resp_length;
304 }
305
306 void
307 soup_message_wrote_informational (SoupMessage *msg)
308 {
309         g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0);
310 }
311
312 void
313 soup_message_wrote_headers (SoupMessage *msg)
314 {
315         g_signal_emit (msg, signals[WROTE_HEADERS], 0);
316 }
317
318 void
319 soup_message_wrote_chunk (SoupMessage *msg)
320 {
321         g_signal_emit (msg, signals[WROTE_CHUNK], 0);
322 }
323
324 static void
325 wrote_body (SoupMessage *req)
326 {
327         g_object_ref (req);
328         soup_message_run_handlers (req, SOUP_HANDLER_POST_REQUEST);
329         g_object_unref (req);
330 }
331
332 void
333 soup_message_wrote_body (SoupMessage *msg)
334 {
335         g_signal_emit (msg, signals[WROTE_BODY], 0);
336 }
337
338 void
339 soup_message_got_informational (SoupMessage *msg)
340 {
341         g_signal_emit (msg, signals[GOT_INFORMATIONAL], 0);
342 }
343
344 static void
345 got_headers (SoupMessage *req)
346 {
347         g_object_ref (req);
348         soup_message_run_handlers (req, SOUP_HANDLER_PRE_BODY);
349         if (SOUP_MESSAGE_IS_STARTING (req))
350                 g_signal_stop_emission (req, signals[GOT_HEADERS], 0);
351         g_object_unref (req);
352 }
353
354 void
355 soup_message_got_headers (SoupMessage *msg)
356 {
357         g_signal_emit (msg, signals[GOT_HEADERS], 0);
358 }
359
360 static void
361 got_chunk (SoupMessage *req)
362 {
363         g_object_ref (req);
364         soup_message_run_handlers (req, SOUP_HANDLER_BODY_CHUNK);
365         if (SOUP_MESSAGE_IS_STARTING (req))
366                 g_signal_stop_emission (req, signals[GOT_CHUNK], 0);
367         g_object_unref (req);
368 }
369
370 void
371 soup_message_got_chunk (SoupMessage *msg)
372 {
373         g_signal_emit (msg, signals[GOT_CHUNK], 0);
374 }
375
376 static void
377 got_body (SoupMessage *req)
378 {
379         g_object_ref (req);
380         soup_message_run_handlers (req, SOUP_HANDLER_POST_BODY);
381         if (SOUP_MESSAGE_IS_STARTING (req))
382                 g_signal_stop_emission (req, signals[GOT_BODY], 0);
383         g_object_unref (req);
384 }
385
386 void
387 soup_message_got_body (SoupMessage *msg)
388 {
389         g_signal_emit (msg, signals[GOT_BODY], 0);
390 }
391
392 static void
393 stop_io (SoupMessage *req)
394 {
395         soup_message_io_cancel (req);
396 }
397
398 void
399 soup_message_restarted (SoupMessage *msg)
400 {
401         g_signal_emit (msg, signals[RESTARTED], 0);
402 }
403
404 void
405 soup_message_finished (SoupMessage *msg)
406 {
407         g_signal_emit (msg, signals[FINISHED], 0);
408 }
409
410 /**
411  * soup_message_cancel:
412  * @msg: a #SoupMessage currently being processed.
413  * 
414  * Cancel a running message, and issue completion callback with an
415  * status code of %SOUP_STATUS_CANCELLED. If not requeued by the
416  * completion callback, the @msg will be destroyed.
417  */
418 void
419 soup_message_cancel (SoupMessage *msg)
420 {
421         if (msg->status != SOUP_MESSAGE_STATUS_FINISHED) {
422                 soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
423                 soup_message_finished (msg);
424         }
425 }
426
427 static gboolean
428 free_header_list (gpointer name, gpointer vals, gpointer user_data)
429 {
430         g_free (name);
431         g_slist_foreach (vals, (GFunc) g_free, NULL);
432         g_slist_free (vals);
433
434         return TRUE;
435 }
436
437 void
438 soup_message_clear_headers (GHashTable *hash)
439 {
440         g_return_if_fail (hash != NULL);
441
442         g_hash_table_foreach_remove (hash, free_header_list, NULL);
443 }
444
445 void
446 soup_message_remove_header (GHashTable *hash, const char *name)
447 {
448         gpointer old_key, old_vals;
449
450         g_return_if_fail (hash != NULL);
451         g_return_if_fail (name != NULL || name[0] != '\0');
452
453         if (g_hash_table_lookup_extended (hash, name, &old_key, &old_vals)) {
454                 g_hash_table_remove (hash, name);
455                 free_header_list (old_key, old_vals, NULL);
456         }
457 }
458
459 void
460 soup_message_add_header (GHashTable *hash, const char *name, const char *value)
461 {
462         GSList *old_value;
463
464         g_return_if_fail (hash != NULL);
465         g_return_if_fail (name != NULL || name [0] != '\0');
466         g_return_if_fail (value != NULL);
467
468         old_value = g_hash_table_lookup (hash, name);
469
470         if (old_value)
471                 g_slist_append (old_value, g_strdup (value));
472         else {
473                 g_hash_table_insert (hash, g_strdup (name),
474                                      g_slist_append (NULL, g_strdup (value)));
475         }
476 }
477
478 /**
479  * soup_message_get_header:
480  * @hash: a header hash table
481  * @name: header name.
482  * 
483  * Lookup the first transport header in @hash with a key equal to
484  * @name.
485  * 
486  * Return value: the header's value or %NULL if not found.
487  */
488 const char *
489 soup_message_get_header (GHashTable *hash, const char *name)
490 {
491         GSList *vals;
492
493         g_return_val_if_fail (hash != NULL, NULL);
494         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
495
496         vals = g_hash_table_lookup (hash, name);
497         if (vals)
498                 return vals->data;
499
500         return NULL;
501 }
502
503 /**
504  * soup_message_get_header_list:
505  * @hash: a header hash table
506  * @name: header name.
507  * 
508  * Lookup the all transport request headers in @hash with a key equal
509  * to @name.
510  * 
511  * Return value: a const pointer to a #GSList of header values or
512  * %NULL if not found.
513  */
514 const GSList *
515 soup_message_get_header_list (GHashTable *hash, const char *name)
516 {
517         g_return_val_if_fail (hash != NULL, NULL);
518         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
519
520         return g_hash_table_lookup (hash, name);
521 }
522
523 typedef struct {
524         GHFunc   func;
525         gpointer user_data;
526 } SoupMessageForeachHeaderData;
527
528 static void
529 foreach_value_in_list (gpointer name, gpointer value, gpointer user_data)
530 {
531         GSList *vals = value;
532         SoupMessageForeachHeaderData *data = user_data;
533
534         while (vals) {
535                 (*data->func) (name, vals->data, data->user_data);
536                 vals = vals->next;
537         }
538 }
539
540 void
541 soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data)
542 {
543         SoupMessageForeachHeaderData data;
544
545         g_return_if_fail (hash != NULL);
546         g_return_if_fail (func != NULL);
547
548         data.func = func;
549         data.user_data = user_data;
550         g_hash_table_foreach (hash, foreach_value_in_list, &data);
551 }
552
553 void
554 soup_message_cleanup_response (SoupMessage *req)
555 {
556         if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
557                 g_free (req->response.body);
558
559         req->response.owner = 0;
560         req->response.body = NULL;
561         req->response.length = 0;
562
563         free_chunks (req);
564
565         soup_message_clear_headers (req->response_headers);
566
567         req->status_code = 0;
568         if (req->reason_phrase) {
569                 g_free ((char *) req->reason_phrase);
570                 req->reason_phrase = NULL;
571         }
572 }
573
574 void
575 soup_message_set_flags (SoupMessage *msg, guint flags)
576 {
577         g_return_if_fail (SOUP_IS_MESSAGE (msg));
578
579         msg->priv->msg_flags = flags;
580 }
581
582 guint
583 soup_message_get_flags (SoupMessage *msg)
584 {
585         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
586
587         return msg->priv->msg_flags;
588 }
589
590 void
591 soup_message_set_http_version (SoupMessage *msg, SoupHttpVersion version)
592 {
593         g_return_if_fail (SOUP_IS_MESSAGE (msg));
594
595         msg->priv->http_version = version;
596 }
597
598 SoupHttpVersion
599 soup_message_get_http_version (SoupMessage *msg)
600 {
601         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0);
602
603         return msg->priv->http_version;
604 }
605
606 gboolean
607 soup_message_is_keepalive (SoupMessage *msg)
608 {
609         const char *c_conn, *s_conn;
610
611         c_conn = soup_message_get_header (msg->request_headers, "Connection");
612         s_conn = soup_message_get_header (msg->response_headers, "Connection");
613
614         if (msg->priv->http_version == SOUP_HTTP_1_0) {
615                 /* Only persistent if the client requested keepalive
616                  * and the server agreed.
617                  */
618
619                 if (!c_conn || !s_conn)
620                         return FALSE;
621                 if (g_strcasecmp (c_conn, "Keep-Alive") != 0 ||
622                     g_strcasecmp (s_conn, "Keep-Alive") != 0)
623                         return FALSE;
624
625                 return TRUE;
626         } else {
627                 /* Persistent unless either side requested otherwise */
628
629                 if (c_conn && g_strcasecmp (c_conn, "close") == 0)
630                         return FALSE;
631                 if (s_conn && g_strcasecmp (s_conn, "close") == 0)
632                         return FALSE;
633
634                 return TRUE;
635         }
636 }
637
638 void
639 soup_message_set_uri (SoupMessage *msg, const SoupUri *new_uri)
640 {
641         g_return_if_fail (SOUP_IS_MESSAGE (msg));
642
643         if (msg->priv->uri && new_uri) {
644                 if (strcmp (msg->priv->uri->host, new_uri->host) != 0)
645                         soup_message_io_cancel (msg);
646         } else if (!new_uri)
647                 soup_message_io_cancel (msg);
648
649         if (msg->priv->uri)
650                 soup_uri_free (msg->priv->uri);
651         msg->priv->uri = soup_uri_copy (new_uri);
652 }
653
654 const SoupUri *
655 soup_message_get_uri (SoupMessage *msg)
656 {
657         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
658
659         return msg->priv->uri;
660 }
661
662 void
663 soup_message_set_status (SoupMessage *msg, guint status_code)
664 {
665         g_return_if_fail (SOUP_IS_MESSAGE (msg));
666         g_return_if_fail (status_code != 0);
667
668         g_free ((char *) msg->reason_phrase);
669
670         msg->status_code = status_code;
671         msg->reason_phrase = g_strdup (soup_status_get_phrase (status_code));
672 }
673
674 void
675 soup_message_set_status_full (SoupMessage *msg,
676                               guint        status_code,
677                               const char  *reason_phrase)
678 {
679         g_return_if_fail (SOUP_IS_MESSAGE (msg));
680         g_return_if_fail (status_code != 0);
681         g_return_if_fail (reason_phrase != NULL);
682
683         g_free ((char *) msg->reason_phrase);
684
685         msg->status_code = status_code;
686         msg->reason_phrase = g_strdup (reason_phrase);
687 }
688
689
690 void
691 soup_message_add_chunk (SoupMessage   *msg,
692                         SoupOwnership  owner,
693                         const char    *body,
694                         guint          length)
695 {
696         SoupDataBuffer *chunk;
697
698         g_return_if_fail (SOUP_IS_MESSAGE (msg));
699         g_return_if_fail (body != NULL || length == 0);
700
701         chunk = g_new0 (SoupDataBuffer, 1);
702         if (owner == SOUP_BUFFER_USER_OWNED) {
703                 chunk->owner = SOUP_BUFFER_SYSTEM_OWNED;
704                 chunk->body = g_memdup (body, length);
705         } else {
706                 chunk->owner = owner;
707                 chunk->body = (char *)body;
708         }
709         chunk->length = length;
710
711         if (msg->priv->chunks) {
712                 g_slist_append (msg->priv->last_chunk, chunk);
713                 msg->priv->last_chunk = msg->priv->last_chunk->next;
714         } else {
715                 msg->priv->chunks = msg->priv->last_chunk =
716                         g_slist_append (NULL, chunk);
717         }
718 }
719
720 void
721 soup_message_add_final_chunk (SoupMessage *msg)
722 {
723         soup_message_add_chunk (msg, SOUP_BUFFER_STATIC, NULL, 0);
724 }
725
726 SoupDataBuffer *
727 soup_message_pop_chunk (SoupMessage *msg)
728 {
729         SoupDataBuffer *chunk;
730
731         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
732
733         if (!msg->priv->chunks)
734                 return NULL;
735
736         chunk = msg->priv->chunks->data;
737         msg->priv->chunks = g_slist_remove (msg->priv->chunks, chunk);
738         if (!msg->priv->chunks)
739                 msg->priv->last_chunk = NULL;
740
741         return chunk;
742 }
743
744 static void
745 free_chunks (SoupMessage *msg)
746 {
747         SoupDataBuffer *chunk;
748         GSList *ch;
749
750         for (ch = msg->priv->chunks; ch; ch = ch->next) {
751                 chunk = ch->data;
752
753                 if (chunk->owner == SOUP_BUFFER_SYSTEM_OWNED)
754                         g_free (chunk->body);
755                 g_free (chunk);
756         }
757
758         g_slist_free (msg->priv->chunks);
759         msg->priv->chunks = msg->priv->last_chunk = NULL;
760 }