rtsp: Added support for HTTP messages
[platform/upstream/gstreamer.git] / gst-libs / gst / rtsp / gstrtspmessage.c
1 /* GStreamer
2  * Copyright (C) <2005,2006> Wim Taymans <wim@fluendo.com>
3  *               <2006> Lutz Mueller <lutz at topfrose dot de>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20 /*
21  * Unless otherwise indicated, Source Code is licensed under MIT license.
22  * See further explanation attached in License Statement (distributed in the file
23  * LICENSE).
24  *
25  * Permission is hereby granted, free of charge, to any person obtaining a copy of
26  * this software and associated documentation files (the "Software"), to deal in
27  * the Software without restriction, including without limitation the rights to
28  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
29  * of the Software, and to permit persons to whom the Software is furnished to do
30  * so, subject to the following conditions:
31  *
32  * The above copyright notice and this permission notice shall be included in all
33  * copies or substantial portions of the Software.
34  *
35  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
41  * SOFTWARE.
42  */
43
44 /**
45  * SECTION:gstrtspmessage
46  * @short_description: RTSP messages
47  * @see_also: gstrtspconnection
48  *  
49  * <refsect2>
50  * <para>
51  * Provides methods for creating and parsing request, response and data messages.
52  * </para>
53  * </refsect2>
54  *  
55  * Last reviewed on 2007-07-25 (0.10.14)
56  */
57
58 #include <string.h>
59
60 #include <gst/gstutils.h>
61 #include "gstrtspmessage.h"
62
63 typedef struct _RTSPKeyValue
64 {
65   GstRTSPHeaderField field;
66   gchar *value;
67 } RTSPKeyValue;
68
69 static void
70 key_value_foreach (GArray * array, GFunc func, gpointer user_data)
71 {
72   guint i;
73
74   g_return_if_fail (array != NULL);
75
76   for (i = 0; i < array->len; i++) {
77     (*func) (&g_array_index (array, RTSPKeyValue, i), user_data);
78   }
79 }
80
81 /**
82  * gst_rtsp_message_new:
83  * @msg: a location for the new #GstRTSPMessage
84  *
85  * Create a new initialized #GstRTSPMessage. Free with gst_rtsp_message_free().
86  *
87  * Returns: a #GstRTSPResult.
88  */
89 GstRTSPResult
90 gst_rtsp_message_new (GstRTSPMessage ** msg)
91 {
92   GstRTSPMessage *newmsg;
93
94   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
95
96   newmsg = g_new0 (GstRTSPMessage, 1);
97
98   *msg = newmsg;
99
100   return gst_rtsp_message_init (newmsg);
101 }
102
103 /**
104  * gst_rtsp_message_init:
105  * @msg: a #GstRTSPMessage
106  *
107  * Initialize @msg. This function is mostly used when @msg is allocated on the
108  * stack. The reverse operation of this is gst_rtsp_message_unset().
109  *
110  * Returns: a #GstRTSPResult.
111  */
112 GstRTSPResult
113 gst_rtsp_message_init (GstRTSPMessage * msg)
114 {
115   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
116
117   gst_rtsp_message_unset (msg);
118
119   msg->type = GST_RTSP_MESSAGE_INVALID;
120   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
121
122   return GST_RTSP_OK;
123 }
124
125 /**
126  * gst_rtsp_message_get_type:
127  * @msg: a #GstRTSPMessage
128  *
129  * Get the message type of @msg.
130  *
131  * Returns: the message type.
132  */
133 GstRTSPMsgType
134 gst_rtsp_message_get_type (GstRTSPMessage * msg)
135 {
136   g_return_val_if_fail (msg != NULL, GST_RTSP_MESSAGE_INVALID);
137
138   return msg->type;
139 }
140
141 /**
142  * gst_rtsp_message_new_request:
143  * @msg: a location for the new #GstRTSPMessage
144  * @method: the request method to use
145  * @uri: the uri of the request
146  *
147  * Create a new #GstRTSPMessage with @method and @uri and store the result
148  * request message in @msg. Free with gst_rtsp_message_free().
149  *
150  * Returns: a #GstRTSPResult.
151  */
152 GstRTSPResult
153 gst_rtsp_message_new_request (GstRTSPMessage ** msg, GstRTSPMethod method,
154     const gchar * uri)
155 {
156   GstRTSPMessage *newmsg;
157
158   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
159   g_return_val_if_fail (uri != NULL, GST_RTSP_EINVAL);
160
161   newmsg = g_new0 (GstRTSPMessage, 1);
162
163   *msg = newmsg;
164
165   return gst_rtsp_message_init_request (newmsg, method, uri);
166 }
167
168 /**
169  * gst_rtsp_message_init_request:
170  * @msg: a #GstRTSPMessage
171  * @method: the request method to use
172  * @uri: the uri of the request
173  *
174  * Initialize @msg as a request message with @method and @uri. To clear @msg
175  * again, use gst_rtsp_message_unset().
176  *
177  * Returns: a #GstRTSPResult.
178  */
179 GstRTSPResult
180 gst_rtsp_message_init_request (GstRTSPMessage * msg, GstRTSPMethod method,
181     const gchar * uri)
182 {
183   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
184   g_return_val_if_fail (uri != NULL, GST_RTSP_EINVAL);
185
186   gst_rtsp_message_unset (msg);
187
188   msg->type = GST_RTSP_MESSAGE_REQUEST;
189   msg->type_data.request.method = method;
190   msg->type_data.request.uri = g_strdup (uri);
191   msg->type_data.request.version = GST_RTSP_VERSION_1_0;
192   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
193
194   return GST_RTSP_OK;
195 }
196
197 /**
198  * gst_rtsp_message_parse_request:
199  * @msg: a #GstRTSPMessage
200  * @method: location to hold the method
201  * @uri: location to hold the uri
202  * @version: location to hold the version
203  *
204  * Parse the request message @msg and store the values @method, @uri and
205  * @version. The result locations can be #NULL if one is not interested in its
206  * value.
207  *
208  * @uri remains valid for as long as @msg is valid and unchanged.
209  *
210  * Returns: a #GstRTSPResult.
211  */
212 GstRTSPResult
213 gst_rtsp_message_parse_request (GstRTSPMessage * msg,
214     GstRTSPMethod * method, const gchar ** uri, GstRTSPVersion * version)
215 {
216   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
217   g_return_val_if_fail (msg->type == GST_RTSP_MESSAGE_REQUEST ||
218       msg->type == GST_RTSP_MESSAGE_HTTP_REQUEST, GST_RTSP_EINVAL);
219
220   if (method)
221     *method = msg->type_data.request.method;
222   if (uri)
223     *uri = msg->type_data.request.uri;
224   if (version)
225     *version = msg->type_data.request.version;
226
227   return GST_RTSP_OK;
228 }
229
230 /**
231  * gst_rtsp_message_new_response:
232  * @msg: a location for the new #GstRTSPMessage
233  * @code: the status code
234  * @reason: the status reason or #NULL
235  * @request: the request that triggered the response or #NULL
236  *
237  * Create a new response #GstRTSPMessage with @code and @reason and store the
238  * result message in @msg. Free with gst_rtsp_message_free().
239  *
240  * When @reason is #NULL, the default reason for @code will be used.
241  *
242  * When @request is not #NULL, the relevant headers will be copied to the new
243  * response message.
244  *
245  * Returns: a #GstRTSPResult.
246  */
247 GstRTSPResult
248 gst_rtsp_message_new_response (GstRTSPMessage ** msg, GstRTSPStatusCode code,
249     const gchar * reason, const GstRTSPMessage * request)
250 {
251   GstRTSPMessage *newmsg;
252
253   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
254
255   newmsg = g_new0 (GstRTSPMessage, 1);
256
257   *msg = newmsg;
258
259   return gst_rtsp_message_init_response (newmsg, code, reason, request);
260 }
261
262 /**
263  * gst_rtsp_message_init_response:
264  * @msg: a #GstRTSPMessage
265  * @code: the status code
266  * @reason: the status reason or #NULL
267  * @request: the request that triggered the response or #NULL
268  *
269  * Initialize @msg with @code and @reason.
270  *
271  * When @reason is #NULL, the default reason for @code will be used.
272  *
273  * When @request is not #NULL, the relevant headers will be copied to the new
274  * response message.
275  *
276  * Returns: a #GstRTSPResult.
277  */
278 GstRTSPResult
279 gst_rtsp_message_init_response (GstRTSPMessage * msg, GstRTSPStatusCode code,
280     const gchar * reason, const GstRTSPMessage * request)
281 {
282   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
283
284   gst_rtsp_message_unset (msg);
285
286   if (reason == NULL)
287     reason = gst_rtsp_status_as_text (code);
288
289   msg->type = GST_RTSP_MESSAGE_RESPONSE;
290   msg->type_data.response.code = code;
291   msg->type_data.response.reason = g_strdup (reason);
292   msg->type_data.response.version = GST_RTSP_VERSION_1_0;
293   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
294
295   if (request) {
296     if (request->type == GST_RTSP_MESSAGE_HTTP_REQUEST) {
297       msg->type = GST_RTSP_MESSAGE_HTTP_RESPONSE;
298       if (request->type_data.request.version != GST_RTSP_VERSION_INVALID)
299         msg->type_data.response.version = request->type_data.request.version;
300       else
301         msg->type_data.response.version = GST_RTSP_VERSION_1_1;
302     } else {
303       gchar *header;
304
305       /* copy CSEQ */
306       if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_CSEQ, &header,
307               0) == GST_RTSP_OK) {
308         gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CSEQ, header);
309       }
310
311       /* copy session id */
312       if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &header,
313               0) == GST_RTSP_OK) {
314         char *pos;
315
316         header = g_strdup (header);
317         if ((pos = strchr (header, ';'))) {
318           *pos = '\0';
319         }
320         g_strchomp (header);
321         gst_rtsp_message_take_header (msg, GST_RTSP_HDR_SESSION, header);
322       }
323
324       /* FIXME copy more headers? */
325     }
326   }
327
328   return GST_RTSP_OK;
329 }
330
331 /**
332  * gst_rtsp_message_parse_response:
333  * @msg: a #GstRTSPMessage
334  * @code: location to hold the status code
335  * @reason: location to hold the status reason
336  * @version: location to hold the version
337  *
338  * Parse the response message @msg and store the values @code, @reason and
339  * @version. The result locations can be #NULL if one is not interested in its
340  * value.
341  *
342  * @reason remains valid for as long as @msg is valid and unchanged.
343  *
344  * Returns: a #GstRTSPResult.
345  */
346 GstRTSPResult
347 gst_rtsp_message_parse_response (GstRTSPMessage * msg,
348     GstRTSPStatusCode * code, const gchar ** reason, GstRTSPVersion * version)
349 {
350   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
351   g_return_val_if_fail (msg->type == GST_RTSP_MESSAGE_RESPONSE ||
352       msg->type == GST_RTSP_MESSAGE_HTTP_RESPONSE, GST_RTSP_EINVAL);
353
354   if (code)
355     *code = msg->type_data.response.code;
356   if (reason)
357     *reason = msg->type_data.response.reason;
358   if (version)
359     *version = msg->type_data.response.version;
360
361   return GST_RTSP_OK;
362 }
363
364 /**
365  * gst_rtsp_message_new_data:
366  * @msg: a location for the new #GstRTSPMessage
367  * @channel: the channel
368  *
369  * Create a new data #GstRTSPMessage with @channel and store the
370  * result message in @msg. Free with gst_rtsp_message_free().
371  *
372  * Returns: a #GstRTSPResult.
373  */
374 GstRTSPResult
375 gst_rtsp_message_new_data (GstRTSPMessage ** msg, guint8 channel)
376 {
377   GstRTSPMessage *newmsg;
378
379   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
380
381   newmsg = g_new0 (GstRTSPMessage, 1);
382
383   *msg = newmsg;
384
385   return gst_rtsp_message_init_data (newmsg, channel);
386 }
387
388 /**
389  * gst_rtsp_message_init_data:
390  * @msg: a #GstRTSPMessage
391  * @channel: a channel
392  *
393  * Initialize a new data #GstRTSPMessage for @channel.
394  *
395  * Returns: a #GstRTSPResult.
396  */
397 GstRTSPResult
398 gst_rtsp_message_init_data (GstRTSPMessage * msg, guint8 channel)
399 {
400   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
401
402   gst_rtsp_message_unset (msg);
403
404   msg->type = GST_RTSP_MESSAGE_DATA;
405   msg->type_data.data.channel = channel;
406
407   return GST_RTSP_OK;
408 }
409
410 /**
411  * gst_rtsp_message_parse_data:
412  * @msg: a #GstRTSPMessage
413  * @channel: location to hold the channel
414  *
415  * Parse the data message @msg and store the channel in @channel.
416  *
417  * Returns: a #GstRTSPResult.
418  */
419 GstRTSPResult
420 gst_rtsp_message_parse_data (GstRTSPMessage * msg, guint8 * channel)
421 {
422   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
423   g_return_val_if_fail (msg->type == GST_RTSP_MESSAGE_DATA, GST_RTSP_EINVAL);
424
425   if (channel)
426     *channel = msg->type_data.data.channel;
427
428   return GST_RTSP_OK;
429 }
430
431 /**
432  * gst_rtsp_message_unset:
433  * @msg: a #GstRTSPMessage
434  *
435  * Unset the contents of @msg so that it becomes an uninitialized
436  * #GstRTSPMessage again. This function is mostly used in combination with 
437  * gst_rtsp_message_init_request(), gst_rtsp_message_init_response() and
438  * gst_rtsp_message_init_data() on stack allocated #GstRTSPMessage structures.
439  *
440  * Returns: #GST_RTSP_OK.
441  */
442 GstRTSPResult
443 gst_rtsp_message_unset (GstRTSPMessage * msg)
444 {
445   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
446
447   switch (msg->type) {
448     case GST_RTSP_MESSAGE_INVALID:
449       break;
450     case GST_RTSP_MESSAGE_REQUEST:
451     case GST_RTSP_MESSAGE_HTTP_REQUEST:
452       g_free (msg->type_data.request.uri);
453       break;
454     case GST_RTSP_MESSAGE_RESPONSE:
455     case GST_RTSP_MESSAGE_HTTP_RESPONSE:
456       g_free (msg->type_data.response.reason);
457       break;
458     case GST_RTSP_MESSAGE_DATA:
459       break;
460     default:
461       g_return_val_if_reached (GST_RTSP_EINVAL);
462   }
463
464   if (msg->hdr_fields != NULL) {
465     guint i;
466
467     for (i = 0; i < msg->hdr_fields->len; i++) {
468       RTSPKeyValue *keyval = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
469
470       g_free (keyval->value);
471     }
472     g_array_free (msg->hdr_fields, TRUE);
473   }
474   g_free (msg->body);
475
476   memset (msg, 0, sizeof (GstRTSPMessage));
477
478   return GST_RTSP_OK;
479 }
480
481 /**
482  * gst_rtsp_message_free:
483  * @msg: a #GstRTSPMessage
484  *
485  * Free the memory used by @msg.
486  *
487  * Returns: a #GstRTSPResult.
488  */
489 GstRTSPResult
490 gst_rtsp_message_free (GstRTSPMessage * msg)
491 {
492   GstRTSPResult res;
493
494   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
495
496   res = gst_rtsp_message_unset (msg);
497   if (res == GST_RTSP_OK)
498     g_free (msg);
499
500   return res;
501 }
502
503 /**
504  * gst_rtsp_message_take_header:
505  * @msg: a #GstRTSPMessage
506  * @field: a #GstRTSPHeaderField
507  * @value: the value of the header
508  *
509  * Add a header with key @field and @value to @msg. This function takes
510  * ownership of @value.
511  *
512  * Returns: a #GstRTSPResult.
513  *
514  * Since: 0.10.23
515  */
516 GstRTSPResult
517 gst_rtsp_message_take_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
518     gchar * value)
519 {
520   RTSPKeyValue key_value;
521
522   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
523   g_return_val_if_fail (value != NULL, GST_RTSP_EINVAL);
524
525   key_value.field = field;
526   key_value.value = value;
527
528   g_array_append_val (msg->hdr_fields, key_value);
529
530   return GST_RTSP_OK;
531 }
532
533 /**
534  * gst_rtsp_message_add_header:
535  * @msg: a #GstRTSPMessage
536  * @field: a #GstRTSPHeaderField
537  * @value: the value of the header
538  *
539  * Add a header with key @field and @value to @msg. This function takes a copy
540  * of @value.
541  *
542  * Returns: a #GstRTSPResult.
543  */
544 GstRTSPResult
545 gst_rtsp_message_add_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
546     const gchar * value)
547 {
548   return gst_rtsp_message_take_header (msg, field, g_strdup (value));
549 }
550
551 /**
552  * gst_rtsp_message_remove_header:
553  * @msg: a #GstRTSPMessage
554  * @field: a #GstRTSPHeaderField
555  * @indx: the index of the header
556  *
557  * Remove the @indx header with key @field from @msg. If @indx equals -1, all
558  * headers will be removed.
559  *
560  * Returns: a #GstRTSPResult.
561  */
562 GstRTSPResult
563 gst_rtsp_message_remove_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
564     gint indx)
565 {
566   GstRTSPResult res = GST_RTSP_ENOTIMPL;
567   guint i = 0;
568   gint cnt = 0;
569
570   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
571
572   while (i < msg->hdr_fields->len) {
573     RTSPKeyValue *key_value = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
574
575     if (key_value->field == field && (indx == -1 || cnt++ == indx)) {
576       g_free (key_value->value);
577       g_array_remove_index (msg->hdr_fields, i);
578       res = GST_RTSP_OK;
579       if (indx != -1)
580         break;
581     } else {
582       i++;
583     }
584   }
585   return res;
586 }
587
588 /**
589  * gst_rtsp_message_get_header:
590  * @msg: a #GstRTSPMessage
591  * @field: a #GstRTSPHeaderField
592  * @value: pointer to hold the result
593  * @indx: the index of the header
594  *
595  * Get the @indx header value with key @field from @msg. The result in @value
596  * stays valid as long as it remains present in @msg.
597  *
598  * Returns: #GST_RTSP_OK when @field was found, #GST_RTSP_ENOTIMPL if the key
599  * was not found.
600  */
601 GstRTSPResult
602 gst_rtsp_message_get_header (const GstRTSPMessage * msg,
603     GstRTSPHeaderField field, gchar ** value, gint indx)
604 {
605   guint i;
606   gint cnt = 0;
607
608   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
609
610   /* no header initialized, there are no headers */
611   if (msg->hdr_fields == NULL)
612     return GST_RTSP_ENOTIMPL;
613
614   for (i = 0; i < msg->hdr_fields->len; i++) {
615     RTSPKeyValue *key_value = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
616
617     if (key_value->field == field && cnt++ == indx) {
618       if (value)
619         *value = key_value->value;
620       return GST_RTSP_OK;
621     }
622   }
623
624   return GST_RTSP_ENOTIMPL;
625 }
626
627 /**
628  * gst_rtsp_message_append_headers:
629  * @msg: a #GstRTSPMessage
630  * @str: a string
631  *
632  * Append the currently configured headers in @msg to the #GString @str suitable
633  * for transmission.
634  *
635  * Returns: #GST_RTSP_OK.
636  */
637 GstRTSPResult
638 gst_rtsp_message_append_headers (const GstRTSPMessage * msg, GString * str)
639 {
640   guint i;
641
642   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
643   g_return_val_if_fail (str != NULL, GST_RTSP_EINVAL);
644
645   for (i = 0; i < msg->hdr_fields->len; i++) {
646     RTSPKeyValue *key_value;
647     const gchar *keystr;
648
649     key_value = &g_array_index (msg->hdr_fields, RTSPKeyValue, i);
650     keystr = gst_rtsp_header_as_text (key_value->field);
651
652     g_string_append_printf (str, "%s: %s\r\n", keystr, key_value->value);
653   }
654   return GST_RTSP_OK;
655 }
656
657 /**
658  * gst_rtsp_message_set_body:
659  * @msg: a #GstRTSPMessage
660  * @data: the data
661  * @size: the size of @data
662  *
663  * Set the body of @msg to a copy of @data.
664  *
665  * Returns: #GST_RTSP_OK.
666  */
667 GstRTSPResult
668 gst_rtsp_message_set_body (GstRTSPMessage * msg, const guint8 * data,
669     guint size)
670 {
671   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
672
673   return gst_rtsp_message_take_body (msg, g_memdup (data, size), size);
674 }
675
676 /**
677  * gst_rtsp_message_take_body:
678  * @msg: a #GstRTSPMessage
679  * @data: the data
680  * @size: the size of @data
681  *
682  * Set the body of @msg to @data and @size. This method takes ownership of
683  * @data.
684  *
685  * Returns: #GST_RTSP_OK.
686  */
687 GstRTSPResult
688 gst_rtsp_message_take_body (GstRTSPMessage * msg, guint8 * data, guint size)
689 {
690   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
691   g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL);
692
693   if (msg->body)
694     g_free (msg->body);
695
696   msg->body = data;
697   msg->body_size = size;
698
699   return GST_RTSP_OK;
700 }
701
702 /**
703  * gst_rtsp_message_get_body:
704  * @msg: a #GstRTSPMessage
705  * @data: location for the data
706  * @size: location for the size of @data
707  *
708  * Get the body of @msg. @data remains valid for as long as @msg is valid and
709  * unchanged.
710  *
711  * Returns: #GST_RTSP_OK.
712  */
713 GstRTSPResult
714 gst_rtsp_message_get_body (const GstRTSPMessage * msg, guint8 ** data,
715     guint * size)
716 {
717   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
718   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
719   g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL);
720
721   *data = msg->body;
722   *size = msg->body_size;
723
724   return GST_RTSP_OK;
725 }
726
727 /**
728  * gst_rtsp_message_steal_body:
729  * @msg: a #GstRTSPMessage
730  * @data: location for the data
731  * @size: location for the size of @data
732  *
733  * Take the body of @msg and store it in @data and @size. After this method,
734  * the body and size of @msg will be set to #NULL and 0 respectively.
735  *
736  * Returns: #GST_RTSP_OK.
737  */
738 GstRTSPResult
739 gst_rtsp_message_steal_body (GstRTSPMessage * msg, guint8 ** data, guint * size)
740 {
741   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
742   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
743   g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL);
744
745   *data = msg->body;
746   *size = msg->body_size;
747
748   msg->body = NULL;
749   msg->body_size = 0;
750
751   return GST_RTSP_OK;
752 }
753
754 static void
755 dump_key_value (gpointer data, gpointer user_data G_GNUC_UNUSED)
756 {
757   RTSPKeyValue *key_value = (RTSPKeyValue *) data;
758
759   g_print ("   key: '%s', value: '%s'\n",
760       gst_rtsp_header_as_text (key_value->field), key_value->value);
761 }
762
763 /**
764  * gst_rtsp_message_dump:
765  * @msg: a #GstRTSPMessage
766  *
767  * Dump the contents of @msg to stdout.
768  *
769  * Returns: #GST_RTSP_OK.
770  */
771 GstRTSPResult
772 gst_rtsp_message_dump (GstRTSPMessage * msg)
773 {
774   guint8 *data;
775   guint size;
776
777   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
778
779   switch (msg->type) {
780     case GST_RTSP_MESSAGE_REQUEST:
781       g_print ("RTSP request message %p\n", msg);
782       g_print (" request line:\n");
783       g_print ("   method: '%s'\n",
784           gst_rtsp_method_as_text (msg->type_data.request.method));
785       g_print ("   uri:    '%s'\n", msg->type_data.request.uri);
786       g_print ("   version: '%s'\n",
787           gst_rtsp_version_as_text (msg->type_data.request.version));
788       g_print (" headers:\n");
789       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
790       g_print (" body:\n");
791       gst_rtsp_message_get_body (msg, &data, &size);
792       gst_util_dump_mem (data, size);
793       break;
794     case GST_RTSP_MESSAGE_RESPONSE:
795       g_print ("RTSP response message %p\n", msg);
796       g_print (" status line:\n");
797       g_print ("   code:   '%d'\n", msg->type_data.response.code);
798       g_print ("   reason: '%s'\n", msg->type_data.response.reason);
799       g_print ("   version: '%s'\n",
800           gst_rtsp_version_as_text (msg->type_data.response.version));
801       g_print (" headers:\n");
802       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
803       gst_rtsp_message_get_body (msg, &data, &size);
804       g_print (" body: length %d\n", size);
805       gst_util_dump_mem (data, size);
806       break;
807     case GST_RTSP_MESSAGE_HTTP_REQUEST:
808       g_print ("HTTP request message %p\n", msg);
809       g_print (" request line:\n");
810       g_print ("   method:  '%s'\n",
811           gst_rtsp_method_as_text (msg->type_data.request.method));
812       g_print ("   uri:     '%s'\n", msg->type_data.request.uri);
813       g_print ("   version: '%s'\n",
814           gst_rtsp_version_as_text (msg->type_data.request.version));
815       g_print (" headers:\n");
816       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
817       g_print (" body:\n");
818       gst_rtsp_message_get_body (msg, &data, &size);
819       gst_util_dump_mem (data, size);
820       break;
821     case GST_RTSP_MESSAGE_HTTP_RESPONSE:
822       g_print ("HTTP response message %p\n", msg);
823       g_print (" status line:\n");
824       g_print ("   code:    '%d'\n", msg->type_data.response.code);
825       g_print ("   reason:  '%s'\n", msg->type_data.response.reason);
826       g_print ("   version: '%s'\n",
827           gst_rtsp_version_as_text (msg->type_data.response.version));
828       g_print (" headers:\n");
829       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
830       gst_rtsp_message_get_body (msg, &data, &size);
831       g_print (" body: length %d\n", size);
832       gst_util_dump_mem (data, size);
833       break;
834     case GST_RTSP_MESSAGE_DATA:
835       g_print ("RTSP data message %p\n", msg);
836       g_print (" channel: '%d'\n", msg->type_data.data.channel);
837       g_print (" size:    '%d'\n", msg->body_size);
838       gst_rtsp_message_get_body (msg, &data, &size);
839       gst_util_dump_mem (data, size);
840       break;
841     default:
842       g_print ("unsupported message type %d\n", msg->type);
843       return GST_RTSP_EINVAL;
844   }
845   return GST_RTSP_OK;
846 }