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