Move SDP and RTSP from helper objects in -good to a reusable library.
[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 #include <string.h>
45
46 #include "gstrtspmessage.h"
47
48 typedef struct _RTSPKeyValue
49 {
50   GstRTSPHeaderField field;
51   gchar *value;
52 } RTSPKeyValue;
53
54 static void
55 key_value_foreach (GArray * array, GFunc func, gpointer user_data)
56 {
57   guint i;
58
59   g_return_if_fail (array != NULL);
60
61   for (i = 0; i < array->len; i++) {
62     (*func) (&g_array_index (array, RTSPKeyValue, i), user_data);
63   }
64 }
65
66 GstRTSPResult
67 gst_rtsp_message_new (GstRTSPMessage ** msg)
68 {
69   GstRTSPMessage *newmsg;
70
71   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
72
73   newmsg = g_new0 (GstRTSPMessage, 1);
74
75   *msg = newmsg;
76
77   return gst_rtsp_message_init (newmsg);
78 }
79
80 GstRTSPResult
81 gst_rtsp_message_init (GstRTSPMessage * msg)
82 {
83   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
84
85   gst_rtsp_message_unset (msg);
86
87   msg->type = GST_RTSP_MESSAGE_INVALID;
88   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
89
90   return GST_RTSP_OK;
91 }
92
93 GstRTSPResult
94 gst_rtsp_message_new_request (GstRTSPMessage ** msg, GstRTSPMethod method,
95     const gchar * uri)
96 {
97   GstRTSPMessage *newmsg;
98
99   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
100   g_return_val_if_fail (uri != NULL, GST_RTSP_EINVAL);
101
102   newmsg = g_new0 (GstRTSPMessage, 1);
103
104   *msg = newmsg;
105
106   return gst_rtsp_message_init_request (newmsg, method, uri);
107 }
108
109 GstRTSPResult
110 gst_rtsp_message_init_request (GstRTSPMessage * msg, GstRTSPMethod method,
111     const gchar * uri)
112 {
113   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
114   g_return_val_if_fail (uri != NULL, GST_RTSP_EINVAL);
115
116   gst_rtsp_message_unset (msg);
117
118   msg->type = GST_RTSP_MESSAGE_REQUEST;
119   msg->type_data.request.method = method;
120   msg->type_data.request.uri = g_strdup (uri);
121   msg->type_data.request.version = GST_RTSP_VERSION_1_0;
122   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
123
124   return GST_RTSP_OK;
125 }
126
127 GstRTSPResult
128 gst_rtsp_message_new_response (GstRTSPMessage ** msg, GstRTSPStatusCode code,
129     const gchar * reason, const GstRTSPMessage * request)
130 {
131   GstRTSPMessage *newmsg;
132
133   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
134
135   newmsg = g_new0 (GstRTSPMessage, 1);
136
137   *msg = newmsg;
138
139   return gst_rtsp_message_init_response (newmsg, code, reason, request);
140 }
141
142 GstRTSPResult
143 gst_rtsp_message_init_response (GstRTSPMessage * msg, GstRTSPStatusCode code,
144     const gchar * reason, const GstRTSPMessage * request)
145 {
146   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
147
148   gst_rtsp_message_unset (msg);
149
150   if (reason == NULL)
151     reason = gst_rtsp_status_as_text (code);
152
153   msg->type = GST_RTSP_MESSAGE_RESPONSE;
154   msg->type_data.response.code = code;
155   msg->type_data.response.reason = g_strdup (reason);
156   msg->type_data.response.version = GST_RTSP_VERSION_1_0;
157   msg->hdr_fields = g_array_new (FALSE, FALSE, sizeof (RTSPKeyValue));
158
159   if (request) {
160     gchar *header;
161
162     /* copy CSEQ */
163     if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_CSEQ, &header,
164             0) == GST_RTSP_OK) {
165       gst_rtsp_message_add_header (msg, GST_RTSP_HDR_CSEQ, header);
166     }
167
168     /* copy session id */
169     if (gst_rtsp_message_get_header (request, GST_RTSP_HDR_SESSION, &header,
170             0) == GST_RTSP_OK) {
171       char *pos;
172
173       header = g_strdup (header);
174       if ((pos = strchr (header, ';'))) {
175         *pos = '\0';
176       }
177       g_strchomp (header);
178       gst_rtsp_message_add_header (msg, GST_RTSP_HDR_SESSION, header);
179       g_free (header);
180     }
181
182     /* FIXME copy more headers? */
183   }
184
185   return GST_RTSP_OK;
186 }
187
188 GstRTSPResult
189 gst_rtsp_message_init_data (GstRTSPMessage * msg, guint8 channel)
190 {
191   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
192
193   gst_rtsp_message_unset (msg);
194
195   msg->type = GST_RTSP_MESSAGE_DATA;
196   msg->type_data.data.channel = channel;
197
198   return GST_RTSP_OK;
199 }
200
201 GstRTSPResult
202 gst_rtsp_message_unset (GstRTSPMessage * msg)
203 {
204   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
205
206   switch (msg->type) {
207     case GST_RTSP_MESSAGE_INVALID:
208       break;
209     case GST_RTSP_MESSAGE_REQUEST:
210       g_free (msg->type_data.request.uri);
211       break;
212     case GST_RTSP_MESSAGE_RESPONSE:
213       g_free (msg->type_data.response.reason);
214       break;
215     case GST_RTSP_MESSAGE_DATA:
216       break;
217     default:
218       g_assert_not_reached ();
219       break;
220   }
221
222   if (msg->hdr_fields != NULL)
223     g_array_free (msg->hdr_fields, TRUE);
224
225   g_free (msg->body);
226
227   memset (msg, 0, sizeof *msg);
228
229   return GST_RTSP_OK;
230 }
231
232 GstRTSPResult
233 gst_rtsp_message_free (GstRTSPMessage * msg)
234 {
235   GstRTSPResult res;
236
237   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
238
239   res = gst_rtsp_message_unset (msg);
240   if (res == GST_RTSP_OK)
241     g_free (msg);
242
243   return res;
244 }
245
246 GstRTSPResult
247 gst_rtsp_message_add_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
248     const gchar * value)
249 {
250   RTSPKeyValue key_value;
251
252   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
253   g_return_val_if_fail (value != NULL, GST_RTSP_EINVAL);
254
255   key_value.field = field;
256   key_value.value = g_strdup (value);
257
258   g_array_append_val (msg->hdr_fields, key_value);
259
260
261   return GST_RTSP_OK;
262 }
263
264 GstRTSPResult
265 gst_rtsp_message_remove_header (GstRTSPMessage * msg, GstRTSPHeaderField field,
266     gint indx)
267 {
268   GstRTSPResult res = GST_RTSP_ENOTIMPL;
269   guint i = 0;
270   gint cnt = 0;
271
272   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
273
274   while (i < msg->hdr_fields->len) {
275     RTSPKeyValue key_value = g_array_index (msg->hdr_fields, RTSPKeyValue, i);
276
277     if (key_value.field == field && (indx == -1 || cnt++ == indx)) {
278       g_array_remove_index (msg->hdr_fields, i);
279       res = GST_RTSP_OK;
280       if (indx != -1)
281         break;
282     } else {
283       i++;
284     }
285   }
286
287   return res;
288 }
289
290 GstRTSPResult
291 gst_rtsp_message_get_header (const GstRTSPMessage * msg,
292     GstRTSPHeaderField field, gchar ** value, gint indx)
293 {
294   guint i;
295   gint cnt = 0;
296
297   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
298
299   for (i = 0; i < msg->hdr_fields->len; i++) {
300     RTSPKeyValue key_value = g_array_index (msg->hdr_fields, RTSPKeyValue, i);
301
302     if (key_value.field == field && cnt++ == indx) {
303       if (value)
304         *value = key_value.value;
305       return GST_RTSP_OK;
306     }
307   }
308
309   return GST_RTSP_ENOTIMPL;
310 }
311
312 GstRTSPResult
313 gst_rtsp_message_append_headers (const GstRTSPMessage * msg, GString * str)
314 {
315   guint i;
316
317   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
318   g_return_val_if_fail (str != NULL, GST_RTSP_EINVAL);
319
320   for (i = 0; i < msg->hdr_fields->len; i++) {
321     RTSPKeyValue key_value = g_array_index (msg->hdr_fields, RTSPKeyValue, i);
322     const gchar *keystr = gst_rtsp_header_as_text (key_value.field);
323
324     g_string_append_printf (str, "%s: %s\r\n", keystr, key_value.value);
325   }
326   return GST_RTSP_OK;
327 }
328
329 GstRTSPResult
330 gst_rtsp_message_set_body (GstRTSPMessage * msg, const guint8 * data,
331     guint size)
332 {
333   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
334
335   return gst_rtsp_message_take_body (msg, g_memdup (data, size), size);
336 }
337
338 GstRTSPResult
339 gst_rtsp_message_take_body (GstRTSPMessage * msg, guint8 * data, guint size)
340 {
341   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
342   g_return_val_if_fail (data != NULL || size == 0, GST_RTSP_EINVAL);
343
344   if (msg->body)
345     g_free (msg->body);
346
347   msg->body = data;
348   msg->body_size = size;
349
350   return GST_RTSP_OK;
351 }
352
353 GstRTSPResult
354 gst_rtsp_message_get_body (const GstRTSPMessage * msg, guint8 ** data,
355     guint * size)
356 {
357   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
358   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
359   g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL);
360
361   *data = msg->body;
362   *size = msg->body_size;
363
364   return GST_RTSP_OK;
365 }
366
367 GstRTSPResult
368 gst_rtsp_message_steal_body (GstRTSPMessage * msg, guint8 ** data, guint * size)
369 {
370   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
371   g_return_val_if_fail (data != NULL, GST_RTSP_EINVAL);
372   g_return_val_if_fail (size != NULL, GST_RTSP_EINVAL);
373
374   *data = msg->body;
375   *size = msg->body_size;
376
377   msg->body = NULL;
378   msg->body_size = 0;
379
380   return GST_RTSP_OK;
381 }
382
383 static void
384 dump_mem (guint8 * mem, guint size)
385 {
386   guint i, j;
387   GString *string = g_string_sized_new (50);
388   GString *chars = g_string_sized_new (18);
389
390   i = j = 0;
391   while (i < size) {
392     if (g_ascii_isprint (mem[i]))
393       g_string_append_printf (chars, "%c", mem[i]);
394     else
395       g_string_append_printf (chars, ".");
396
397     g_string_append_printf (string, "%02x ", mem[i]);
398
399     j++;
400     i++;
401
402     if (j == 16 || i == size) {
403       g_print ("%08x (%p): %-48.48s %-16.16s\n", i - j, mem + i - j,
404           string->str, chars->str);
405       g_string_set_size (string, 0);
406       g_string_set_size (chars, 0);
407       j = 0;
408     }
409   }
410   g_string_free (string, TRUE);
411   g_string_free (chars, TRUE);
412 }
413
414 static void
415 dump_key_value (gpointer data, gpointer user_data)
416 {
417   RTSPKeyValue *key_value = (RTSPKeyValue *) data;
418
419   g_print ("   key: '%s', value: '%s'\n",
420       gst_rtsp_header_as_text (key_value->field), key_value->value);
421 }
422
423 GstRTSPResult
424 gst_rtsp_message_dump (GstRTSPMessage * msg)
425 {
426   guint8 *data;
427   guint size;
428
429   g_return_val_if_fail (msg != NULL, GST_RTSP_EINVAL);
430
431   switch (msg->type) {
432     case GST_RTSP_MESSAGE_REQUEST:
433       g_print ("RTSP request message %p\n", msg);
434       g_print (" request line:\n");
435       g_print ("   method: '%s'\n",
436           gst_rtsp_method_as_text (msg->type_data.request.method));
437       g_print ("   uri:    '%s'\n", msg->type_data.request.uri);
438       g_print ("   version: '%s'\n",
439           gst_rtsp_version_as_text (msg->type_data.request.version));
440       g_print (" headers:\n");
441       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
442       g_print (" body:\n");
443       gst_rtsp_message_get_body (msg, &data, &size);
444       dump_mem (data, size);
445       break;
446     case GST_RTSP_MESSAGE_RESPONSE:
447       g_print ("RTSP response message %p\n", msg);
448       g_print (" status line:\n");
449       g_print ("   code:   '%d'\n", msg->type_data.response.code);
450       g_print ("   reason: '%s'\n", msg->type_data.response.reason);
451       g_print ("   version: '%s'\n",
452           gst_rtsp_version_as_text (msg->type_data.response.version));
453       g_print (" headers:\n");
454       key_value_foreach (msg->hdr_fields, dump_key_value, NULL);
455       gst_rtsp_message_get_body (msg, &data, &size);
456       g_print (" body: length %d\n", size);
457       dump_mem (data, size);
458       break;
459     case GST_RTSP_MESSAGE_DATA:
460       g_print ("RTSP data message %p\n", msg);
461       g_print (" channel: '%d'\n", msg->type_data.data.channel);
462       g_print (" size:    '%d'\n", msg->body_size);
463       gst_rtsp_message_get_body (msg, &data, &size);
464       dump_mem (data, size);
465       break;
466     default:
467       g_print ("unsupported message type %d\n", msg->type);
468       return GST_RTSP_EINVAL;
469   }
470   return GST_RTSP_OK;
471 }