2
* The contents of this file are subject to the Mozilla Public
3
* License Version 1.1 (the "License"); you may not use this file
4
* except in compliance with the License. You may obtain a copy of
5
* the License at http://www.mozilla.org/MPL/
7
* Software distributed under the License is distributed on an "AS
8
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9
* implied. See the License for the specific language governing
10
* rights and limitations under the License.
12
* The Original Code is MPEG4IP.
14
* The Initial Developer of the Original Code is Cisco Systems Inc.
15
* Portions created by Cisco Systems Inc. are
16
* Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
19
* Bill May wmay@cisco.com
22
* rtsp_command.c - process API calls to send/receive rtsp commands
24
#include "rtsp_private.h"
28
static const char *UserAgent = "Cisco RTSP 1.0";
32
* Builds a common header based on rtsp_command_t information.
34
static int rtsp_build_common (char *buffer,
44
* The below is ugly, but it will allow us to remove a lot of lines
45
* of code. SNPRINTF_CHECK makes sure (for this routine), that we
46
* don't have more data in the buffer than allowed - it will return
47
* an error code if that happens.
49
#define SNPRINTF_CHECK(fmt, value) \
50
ret = snprintf(buffer + *at, maxlen - *at, (fmt), (value)); \
56
SNPRINTF_CHECK("CSeq: %u\r\n", info->next_cseq);
58
SNPRINTF_CHECK("Cookie: %s\r\n", info->cookie);
61
if (cmd && cmd->accept) {
62
SNPRINTF_CHECK("Accept: %s\r\n", cmd->accept);
64
if (cmd && cmd->accept_encoding) {
65
SNPRINTF_CHECK("Accept-Encoding: %s\r\n", cmd->accept_encoding);
67
if (cmd && cmd->accept_language) {
68
SNPRINTF_CHECK("Accept-Language: %s\r\n", cmd->accept_language);
70
if (cmd && cmd->authorization) {
71
SNPRINTF_CHECK("Authorization: %s\r\n", cmd->authorization);
73
if (cmd && cmd->bandwidth != 0) {
74
SNPRINTF_CHECK("Bandwidth: %u\r\n", cmd->bandwidth);
76
if (cmd && cmd->blocksize != 0) {
77
SNPRINTF_CHECK("Blocksize: %u\r\n", cmd->blocksize);
79
if (cmd && cmd->cachecontrol) {
80
SNPRINTF_CHECK("Cache-Control: %s\r\n", cmd->cachecontrol);
82
if (cmd && cmd->conference) {
83
SNPRINTF_CHECK("Conference: %s\r\n", cmd->conference);
85
if (cmd && cmd->from) {
86
SNPRINTF_CHECK("From: %s\r\n", cmd->from);
88
if (cmd && cmd->proxyauth) {
89
SNPRINTF_CHECK("Proxy-Authorization: %s\r\n", cmd->proxyauth);
91
if (cmd && cmd->proxyrequire) {
92
SNPRINTF_CHECK("Proxy-Require: %s\r\n", cmd->proxyrequire);
94
if (cmd && cmd->range) {
95
SNPRINTF_CHECK("Range: %s\r\n", cmd->range);
97
if (cmd && cmd->referer) {
98
SNPRINTF_CHECK("Referer: %s\r\n", cmd->referer);
100
if (cmd && cmd->scale != 0.0) {
101
SNPRINTF_CHECK("Scale: %f\r\n", cmd->scale);
104
SNPRINTF_CHECK("Session: %s\r\n", session);
105
} else if (cmd && cmd->session) {
106
SNPRINTF_CHECK("Session: %s\r\n", cmd->session);
108
if (cmd && cmd->speed != 0.0) {
109
SNPRINTF_CHECK("Speed: %f\r\n", cmd->speed);
111
if (cmd && cmd->transport) {
112
SNPRINTF_CHECK("Transport: %s\r\n", cmd->transport);
115
SNPRINTF_CHECK("User-Agent: %s\r\n",
116
(cmd && cmd->useragent != NULL ? cmd->useragent : UserAgent));
117
if (cmd && cmd->User) {
118
SNPRINTF_CHECK("%s", cmd->User);
120
#undef SNPRINTF_CHECK
125
* rtsp_send_describe - send the describe info to a server
127
int rtsp_send_describe (rtsp_client_t *info,
129
rtsp_decode_t **decode_result)
132
uint32_t maxlen, buflen;
134
rtsp_decode_t *decode;
136
*decode_result = NULL;
137
info->redirect_count = 0;
140
maxlen = sizeof(buffer);
141
buflen = snprintf(buffer, maxlen, "DESCRIBE %s RTSP/1.0\r\n", info->url);
143
if (rtsp_build_common(buffer, maxlen, &buflen, info, cmd, NULL) == -1) {
144
return (RTSP_RESPONSE_RECV_ERROR);
147
ret = snprintf(buffer + buflen, maxlen - buflen, "\r\n");
149
return (RTSP_RESPONSE_RECV_ERROR);
153
rtsp_debug(LOG_INFO, "Sending DESCRIBE %s", info->url);
154
rtsp_debug(LOG_DEBUG, "%s", buffer);
156
ret = rtsp_send_and_get(info, buffer, buflen);
157
decode = info->decode_response;
159
if (ret == RTSP_RESPONSE_GOOD) {
160
rtsp_debug(LOG_INFO, "DESCRIBE returned correctly");
161
*decode_result = info->decode_response;
162
info->decode_response = NULL;
163
return (RTSP_RESPONSE_GOOD);
164
} else if (ret != RTSP_RESPONSE_REDIRECT) {
166
rtsp_debug(LOG_ERR, "DESCRIBE return code %d", ret);
167
if (ret != RTSP_RESPONSE_RECV_ERROR &&
169
*decode_result = info->decode_response;
170
info->decode_response = NULL;
171
rtsp_debug(LOG_ERR, "Error code %s %s",
178
* Handle this through the redirects
180
} while (ret == RTSP_RESPONSE_REDIRECT);
182
return (RTSP_RESPONSE_RECV_ERROR);
186
* rtsp_send_setup - When we get the describe, this will set up a
187
* particular stream. Use the session handle for all further commands for
188
* the stream (play, pause, teardown).
190
int rtsp_send_setup (rtsp_client_t *info,
193
rtsp_session_t **session_result,
194
rtsp_decode_t **decode_result,
197
char buffer[2048], *temp;
198
uint32_t maxlen, buflen;
200
rtsp_decode_t *decode;
201
rtsp_session_t *sptr;
203
*decode_result = NULL;
204
*session_result = NULL;
205
info->redirect_count = 0;
207
if (cmd == NULL || cmd->transport == NULL || url == NULL) {
208
return (RTSP_RESPONSE_MISSING_OR_BAD_PARAM);
211
if (strncmp(url, "rtsp://", strlen("rtsp://")) != 0) {
212
return (RTSP_RESPONSE_BAD_URL);
215
temp = strchr(url + strlen("rtsp://"), '/');
217
return (RTSP_RESPONSE_BAD_URL);
219
if (strncmp(url, info->url, temp - url) != 0) {
220
rtsp_debug(LOG_ALERT, "Bad url %s", url);
221
rtsp_debug(LOG_ALERT, "Should be %s", info->url);
222
return (RTSP_RESPONSE_BAD_URL);
225
maxlen = sizeof(buffer);
226
buflen = snprintf(buffer, maxlen, "SETUP %s RTSP/1.0\r\n", url);
228
// always use the session here...
229
if (rtsp_build_common(buffer,
234
info->session) == -1) {
235
return (RTSP_RESPONSE_RECV_ERROR);
237
ret = snprintf(buffer + buflen, maxlen - buflen, "\r\n");
239
return (RTSP_RESPONSE_RECV_ERROR);
243
rtsp_debug(LOG_INFO, "Sending SETUP %s", url);
244
rtsp_debug(LOG_DEBUG, "%s", buffer);
246
ret = rtsp_send_and_get(info, buffer, buflen);
247
decode = info->decode_response;
249
if (ret == RTSP_RESPONSE_GOOD) {
250
rtsp_debug(LOG_INFO, "SETUP returned correctly");
251
*decode_result = info->decode_response;
252
info->decode_response = NULL;
253
#ifndef IPTV_COMPATIBLE
254
if ((*decode_result)->session == NULL) {
255
return (RTSP_RESPONSE_BAD);
258
if (is_aggregate && info->session != NULL) {
259
if (strcmp(info->session, (*decode_result)->session) != 0) {
260
rtsp_debug(LOG_ALERT, "Session for %s returned different %s %s",
261
url, info->session, (*decode_result)->session);
262
return (RTSP_RESPONSE_BAD);
265
sptr = info->session_list;
266
// we really need to seperate out the session from the
267
// rest of the Session: header
268
while (sptr != NULL) {
269
if (strcmp(sptr->url, url) == 0)
274
sptr = malloc(sizeof(rtsp_session_t));
276
return (RTSP_RESPONSE_RECV_ERROR);
278
sptr->url = strdup(url);
279
if ((*decode_result)->session != NULL)
280
sptr->session = strdup((*decode_result)->session);
282
sptr->session = NULL;
284
sptr->next = info->session_list;
285
info->session_list = sptr;
286
info->session = sptr->session;
288
*session_result = sptr;
289
return (RTSP_RESPONSE_GOOD);
291
rtsp_debug(LOG_ERR, "SETUP return code %d", ret);
292
if (ret != RTSP_RESPONSE_RECV_ERROR &&
294
*decode_result = info->decode_response;
295
info->decode_response = NULL;
296
rtsp_debug(LOG_ERR, "Error code %s %s",
303
return (RTSP_RESPONSE_RECV_ERROR);
307
* check_session - make sure that the session is correct for that command
309
static int check_session (rtsp_session_t *session,
312
rtsp_session_t *sptr;
315
info = session->parent;
317
rtsp_debug(LOG_ALERT, "Session doesn't point to parent");
321
sptr = info->session_list;
322
while (sptr != session && sptr != NULL) sptr = sptr->next;
324
rtsp_debug(LOG_ALERT, "session not found in info list");
329
(cmd->session != NULL) &&
330
(strcmp(cmd->session, session->session) != 0)) {
331
rtsp_debug(LOG_ALERT, "Have cmd->session set wrong");
337
static int rtsp_send_play_or_pause (const char *command,
342
rtsp_decode_t **decode_result)
345
uint32_t maxlen, buflen;
347
rtsp_decode_t *decode;
349
*decode_result = NULL;
350
if (info->server_socket < 0) {
351
return (RTSP_RESPONSE_CLOSED_SOCKET);
354
maxlen = sizeof(buffer);
355
buflen = snprintf(buffer, maxlen, "%s %s RTSP/1.0\r\n", command, url);
357
if (rtsp_build_common(buffer, maxlen, &buflen,
358
info, cmd, session) == -1) {
359
return (RTSP_RESPONSE_RECV_ERROR);
362
ret = snprintf(buffer + buflen, maxlen - buflen, "\r\n");
364
return (RTSP_RESPONSE_RECV_ERROR);
368
rtsp_debug(LOG_INFO, "Sending %s %s", command, url);
369
rtsp_debug(LOG_DEBUG, "%s", buffer);
371
ret = rtsp_send_and_get(info, buffer, buflen);
372
decode = info->decode_response;
374
if (ret == RTSP_RESPONSE_GOOD) {
375
rtsp_debug(LOG_ERR, "%s returned correctly", command);
376
*decode_result = info->decode_response;
377
info->decode_response = NULL;
379
return (RTSP_RESPONSE_GOOD);
381
rtsp_debug(LOG_ERR, "%s return code %d", command, ret);
382
if (ret != RTSP_RESPONSE_RECV_ERROR &&
384
*decode_result = info->decode_response;
385
info->decode_response = NULL;
386
rtsp_debug(LOG_ERR, "Error code %s %s",
393
return (RTSP_RESPONSE_RECV_ERROR);
397
* rtsp_send_play - send play command. It helps if Range is set
399
int rtsp_send_play (rtsp_session_t *session,
401
rtsp_decode_t **decode_result)
403
if (check_session(session, cmd) == FALSE) {
404
return (RTSP_RESPONSE_MISSING_OR_BAD_PARAM);
407
return (rtsp_send_play_or_pause("PLAY",
416
* rtsp_send_pause - send a pause on a particular session
418
int rtsp_send_pause (rtsp_session_t *session,
420
rtsp_decode_t **decode_result)
422
if (check_session(session, cmd) == FALSE) {
423
return (RTSP_RESPONSE_MISSING_OR_BAD_PARAM);
426
return (rtsp_send_play_or_pause("PAUSE",
434
int rtsp_send_aggregate_play (rtsp_client_t *info,
435
const char *aggregate_url,
437
rtsp_decode_t **decode_result)
439
return (rtsp_send_play_or_pause("PLAY",
447
int rtsp_send_aggregate_pause (rtsp_client_t *info,
448
const char *aggregate_url,
450
rtsp_decode_t **decode_result)
452
return (rtsp_send_play_or_pause("PAUSE",
460
static int rtsp_send_teardown_common (rtsp_client_t *info,
464
rtsp_decode_t **decode_result)
467
uint32_t maxlen, buflen;
469
rtsp_decode_t *decode;
471
*decode_result = NULL;
472
if (info->server_socket < 0) {
473
return (RTSP_RESPONSE_CLOSED_SOCKET);
476
maxlen = sizeof(buffer);
477
buflen = snprintf(buffer, maxlen, "TEARDOWN %s RTSP/1.0\r\n", url);
479
if (rtsp_build_common(buffer, maxlen, &buflen,
480
info, cmd, session) == -1) {
481
return (RTSP_RESPONSE_RECV_ERROR);
484
ret = snprintf(buffer + buflen, maxlen - buflen, "\r\n");
486
return (RTSP_RESPONSE_RECV_ERROR);
490
rtsp_debug(LOG_INFO, "Sending TEARDOWN %s", url);
491
rtsp_debug(LOG_DEBUG, "%s", buffer);
493
ret = rtsp_send_and_get(info, buffer, buflen);
494
decode = info->decode_response;
496
if (ret == RTSP_RESPONSE_GOOD) {
497
rtsp_debug(LOG_INFO, "TEARDOWN returned correctly");
498
*decode_result = info->decode_response;
499
info->decode_response = NULL;
500
return (RTSP_RESPONSE_GOOD);
502
rtsp_debug(LOG_ERR, "TEARDOWN return code %d", ret);
503
if (ret != RTSP_RESPONSE_RECV_ERROR &&
505
*decode_result = info->decode_response;
506
info->decode_response = NULL;
507
rtsp_debug(LOG_ERR, "Error code %s %s",
514
return (RTSP_RESPONSE_RECV_ERROR);
517
* rtsp_send_teardown. Sends a teardown for a session. We might eventually
518
* want to provide a teardown for the base url, rather than one for each
521
int rtsp_send_teardown (rtsp_session_t *session,
523
rtsp_decode_t **decode_result)
527
rtsp_session_t *sptr;
528
if (check_session(session, cmd) == FALSE) {
529
return (RTSP_RESPONSE_MISSING_OR_BAD_PARAM);
531
info = session->parent;
533
ret = rtsp_send_teardown_common(info,
538
if (ret == RTSP_RESPONSE_GOOD) {
539
if (info->session_list == session) {
540
info->session_list = session->next;
542
sptr = info->session_list;
543
while (sptr->next != session) sptr = sptr->next;
544
sptr->next = session->next;
546
free_session_info(session);
551
int rtsp_send_aggregate_teardown (rtsp_client_t *info,
554
rtsp_decode_t **decode_result)
558
ret = rtsp_send_teardown_common(info,
563
if (ret == RTSP_RESPONSE_GOOD) {
564
while (info->session_list != NULL) {
565
p = info->session_list;
566
info->session_list = info->session_list->next;
567
free_session_info(p);