Coverage Report

Created: 2026-01-09 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/ietf/rtsp_command.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2000-2022
6
 *          All rights reserved
7
 *
8
 *  This file is part of GPAC / IETF RTP/RTSP/SDP sub-project
9
 *
10
 *  GPAC is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU Lesser General Public License as published by
12
 *  the Free Software Foundation; either version 2, or (at your option)
13
 *  any later version.
14
 *
15
 *  GPAC is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU Lesser General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU Lesser General Public
21
 *  License along with this library; see the file COPYING.  If not, write to
22
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
 *
24
 */
25
26
27
#include <gpac/internal/ietf_dev.h>
28
29
#ifndef GPAC_DISABLE_STREAMING
30
31
#include <gpac/token.h>
32
#include <gpac/base_coding.h>
33
34
GF_EXPORT
35
GF_RTSPCommand *gf_rtsp_command_new()
36
0
{
37
0
  GF_RTSPCommand *tmp;
38
0
  GF_SAFEALLOC(tmp, GF_RTSPCommand);
39
0
  if (!tmp) return NULL;
40
0
  tmp->Xtensions = gf_list_new();
41
0
  tmp->Transports = gf_list_new();
42
0
  return tmp;
43
0
}
44
45
46
0
#define COM_FREE_CLEAN(hdr)   if (com->hdr) gf_free(com->hdr); \
47
0
                com->hdr = NULL;
48
49
GF_EXPORT
50
void gf_rtsp_command_reset(GF_RTSPCommand *com)
51
0
{
52
0
  if (!com) return;
53
54
  //free all headers
55
0
  COM_FREE_CLEAN(Accept);
56
0
  COM_FREE_CLEAN(Accept_Encoding);
57
0
  COM_FREE_CLEAN(Accept_Language);
58
0
  COM_FREE_CLEAN(Authorization);
59
0
  COM_FREE_CLEAN(Cache_Control);
60
0
  COM_FREE_CLEAN(Conference);
61
0
  COM_FREE_CLEAN(Connection);
62
0
  COM_FREE_CLEAN(From);
63
0
  COM_FREE_CLEAN(Proxy_Authorization);
64
0
  COM_FREE_CLEAN(Proxy_Require);
65
0
  COM_FREE_CLEAN(Referer);
66
0
  COM_FREE_CLEAN(Session);
67
0
  COM_FREE_CLEAN(User_Agent);
68
0
  COM_FREE_CLEAN(body);
69
0
  COM_FREE_CLEAN(service_name);
70
0
  COM_FREE_CLEAN(ControlString);
71
0
  COM_FREE_CLEAN(method);
72
73
  //this is for server only, set to OK by default
74
0
  com->StatusCode = NC_RTSP_OK;
75
76
77
0
  com->user_data = NULL;
78
79
0
  com->Bandwidth = com->Blocksize = com->Content_Length = com->CSeq = 0;
80
0
  com->Scale = com->Speed = 0.0;
81
0
  if (com->Range) gf_free(com->Range);
82
0
  com->Range = NULL;
83
84
0
  while (gf_list_count(com->Transports)) {
85
0
    GF_RTSPTransport *trans = (GF_RTSPTransport *) gf_list_get(com->Transports, 0);
86
0
    gf_list_rem(com->Transports, 0);
87
0
    gf_rtsp_transport_del(trans);
88
0
  }
89
0
  while (gf_list_count(com->Xtensions)) {
90
0
    GF_X_Attribute *att = (GF_X_Attribute*)gf_list_get(com->Xtensions, 0);
91
0
    gf_list_rem(com->Xtensions, 0);
92
0
    gf_free(att->Name);
93
0
    gf_free(att->Value);
94
0
    gf_free(att);
95
0
  }
96
0
}
97
98
GF_EXPORT
99
void gf_rtsp_command_del(GF_RTSPCommand *com)
100
0
{
101
0
  if (!com) return;
102
0
  gf_rtsp_command_reset(com);
103
0
  gf_list_del(com->Xtensions);
104
0
  gf_list_del(com->Transports);
105
0
  gf_free(com);
106
0
}
107
108
109
GF_Err RTSP_WriteCommand(GF_RTSPSession *sess, GF_RTSPCommand *com, unsigned char *req_buffer,
110
                         unsigned char **out_buffer, u32 *out_size)
111
0
{
112
0
  u32 i, cur_pos, size, count;
113
0
  char *buffer;
114
115
0
  *out_buffer = NULL;
116
  
117
0
  size = RTSP_WRITE_STEPALLOC;
118
0
  buffer = (char *) gf_malloc(size);
119
0
  cur_pos = 0;
120
121
  //request
122
0
  RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, req_buffer);
123
124
  //then all headers
125
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept", com->Accept);
126
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Encoding", com->Accept_Encoding);
127
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "Accept-Language", com->Accept_Language);
128
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "Authorization", com->Authorization);
129
0
  if (com->Bandwidth) {
130
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Bandwidth: ");
131
0
    RTSP_WRITE_INT(buffer, size, cur_pos, com->Bandwidth, 0);
132
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
133
0
  }
134
0
  if (com->Blocksize) {
135
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Blocksize: ");
136
0
    RTSP_WRITE_INT(buffer, size, cur_pos, com->Blocksize, 0);
137
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
138
0
  }
139
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "Cache-Control", com->Cache_Control);
140
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "Conference", com->Conference);
141
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "Connection", com->Connection);
142
  //if we have a body write the content length
143
0
  if (com->body) {
144
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Content-Length: ");
145
0
    RTSP_WRITE_INT(buffer, size, cur_pos, (u32) strlen(com->body), 0);
146
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
147
0
  }
148
  //write the CSeq - use the SESSION CSeq
149
0
  RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "CSeq: ");
150
0
  RTSP_WRITE_INT(buffer, size, cur_pos, sess->CSeq, 0);
151
0
  RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
152
153
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "From", com->From);
154
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Authorization", com->Proxy_Authorization);
155
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "Proxy-Require", com->Proxy_Require);
156
157
  //Range, only NPT
158
0
  if (com->Range && !com->Range->UseSMPTE) {
159
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Range: npt=");
160
0
    RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Range->start);
161
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
162
0
    if (com->Range->end > com->Range->start) {
163
0
      RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Range->end);
164
0
    }
165
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
166
0
  }
167
168
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "Referer", com->Referer);
169
0
  if (com->Scale != 0.0) {
170
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Scale: ");
171
0
    RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Scale);
172
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
173
0
  }
174
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "Session", com->Session);
175
0
  if (com->Speed != 0.0) {
176
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Speed: ");
177
0
    RTSP_WRITE_FLOAT_WITHOUT_CHECK(buffer, size, cur_pos, com->Speed);
178
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
179
0
  }
180
181
  //transport info
182
0
  count = gf_list_count(com->Transports);
183
0
  if (count) {
184
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "Transport: ");
185
0
    for (i=0; i<count; i++) {
186
0
      GF_RTSPTransport *trans;
187
      //line separator for headers
188
0
      if (i) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n ,");
189
0
      trans = (GF_RTSPTransport *) gf_list_get(com->Transports, i);
190
191
      //then write the structure
192
0
      RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->Profile);
193
0
      RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, (trans->IsUnicast ? ";unicast" : ";multicast"));
194
0
      if (trans->destination) {
195
0
        RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";destination=");
196
0
        RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->destination);
197
0
      }
198
0
      if (trans->source) {
199
0
        RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";source=");
200
0
        RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, trans->source);
201
0
      }
202
0
      if (trans->IsRecord) {
203
0
        RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";mode=RECORD");
204
0
        if (trans->Append) RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";append");
205
0
      }
206
0
      if (trans->IsInterleaved) {
207
0
        RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";interleaved=");
208
0
        RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtpID, 0);
209
0
        if (trans->rtcpID != trans->rtpID) {
210
0
          RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
211
0
          RTSP_WRITE_INT(buffer, size, cur_pos, trans->rtcpID, 0);
212
0
        }
213
0
      }
214
0
      if (trans->port_first) {
215
0
        RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, (trans->IsUnicast ? ";server_port=" : ";port="));
216
0
        RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_first, 0);
217
0
        RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
218
0
        RTSP_WRITE_INT(buffer, size, cur_pos, trans->port_last, 0);
219
0
      }
220
0
      if (/*trans->IsUnicast && */trans->client_port_first) {
221
0
        RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";client_port=");
222
0
        RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_first, 0);
223
0
        RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "-");
224
0
        RTSP_WRITE_INT(buffer, size, cur_pos, trans->client_port_last, 0);
225
0
      }
226
      //multicast specific
227
0
      if (!trans->IsUnicast) {
228
0
        if (trans->MulticastLayers) {
229
0
          RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";layers=");
230
0
          RTSP_WRITE_INT(buffer, size, cur_pos, trans->MulticastLayers, 0);
231
0
        }
232
0
        if (trans->TTL) {
233
0
          RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ttl=");
234
0
          RTSP_WRITE_INT(buffer, size, cur_pos, trans->TTL, 0);
235
0
        }
236
0
      }
237
0
      if (trans->SSRC) {
238
0
        RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, ";ssrc=");
239
0
        RTSP_WRITE_HEX(buffer, size, cur_pos, trans->SSRC, 0);
240
0
      }
241
0
    }
242
    //done with transport
243
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
244
0
  }
245
0
  RTSP_WRITE_HEADER(buffer, size, cur_pos, "User-Agent", com->User_Agent);
246
247
  //eXtensions
248
0
  count = gf_list_count(com->Xtensions);
249
0
  for (i=0; i<count; i++) {
250
0
    GF_X_Attribute *att = (GF_X_Attribute *) gf_list_get(com->Xtensions, i);
251
0
    RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "x-");
252
0
    RTSP_WRITE_HEADER(buffer, size, cur_pos, att->Name, att->Value);
253
0
  }
254
255
  //the end of header
256
0
  RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
257
  //then body
258
0
  RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, com->body);
259
  //the end of message ? to check, should not be needed...
260
//  RTSP_WRITE_ALLOC_STR(buffer, size, cur_pos, "\r\n");
261
262
0
  *out_buffer = (unsigned char *)buffer;
263
0
  *out_size = (u32) strlen(buffer);
264
0
  return GF_OK;
265
0
}
266
267
268
//format a DESCRIBE, SETUP, PLAY or PAUSE on a session
269
//YOUR COMMAND MUST BE FORMATTED ACCORDINGLY
270
//sCtrl contains a control string if needed, formatting the REQUEST as server_url/service_name/sCtrl
271
GF_EXPORT
272
GF_Err gf_rtsp_send_command(GF_RTSPSession *sess, GF_RTSPCommand *com)
273
0
{
274
0
  GF_Err e;
275
0
  char *sCtrl;
276
0
  const char *rad;
277
0
  u32 size;
278
0
  char buffer[1024], *result, *body;
279
280
0
  if (!com || !com->method) return GF_BAD_PARAM;
281
282
0
  sCtrl = com->ControlString;
283
284
  //NB: OPTIONS is not sent this way
285
0
  if (strcmp(com->method, GF_RTSP_DESCRIBE)
286
0
          && strcmp(com->method, GF_RTSP_ANNOUNCE)
287
0
          && strcmp(com->method, GF_RTSP_GET_PARAMETER)
288
0
          && strcmp(com->method, GF_RTSP_SET_PARAMETER)
289
0
          && strcmp(com->method, GF_RTSP_SETUP)
290
0
          && strcmp(com->method, GF_RTSP_PLAY)
291
0
          && strcmp(com->method, GF_RTSP_PAUSE)
292
0
          && strcmp(com->method, GF_RTSP_RECORD)
293
0
          && strcmp(com->method, GF_RTSP_REDIRECT)
294
0
          && strcmp(com->method, GF_RTSP_TEARDOWN)
295
0
          && strcmp(com->method, GF_RTSP_OPTIONS)
296
297
0
     ) return GF_BAD_PARAM;
298
299
  //check the state machine
300
0
  if (strcmp(com->method, GF_RTSP_PLAY)
301
0
          && strcmp(com->method, GF_RTSP_PAUSE)
302
0
          && strcmp(com->method, GF_RTSP_RECORD)
303
0
          && sess->RTSP_State != GF_RTSP_STATE_INIT)
304
0
    return GF_SERVICE_ERROR;
305
306
  //aggregation is ONLY for the same request - unclear in RFC2326 ...
307
  //it is often mentioned "queued requests" at the server, like 3 PLAYS
308
  //and a PAUSE ....
309
310
  /*
311
  else if (sess->RTSP_State == GF_RTSP_STATE_WAIT_FOR_CONTROL
312
    && strcmp(com->method, sess->RTSPLastRequest))
313
    && strcmp(com->method, GF_RTSP_OPTIONS))
314
315
    return GF_BAD_PARAM;
316
  */
317
318
  //OPTIONS must have a parameter string
319
0
  if (!strcmp(com->method, GF_RTSP_OPTIONS) && !sCtrl) return GF_BAD_PARAM;
320
321
322
  //update sequence number
323
0
  if (!com->is_resend) {
324
0
    sess->CSeq += 1;
325
0
    sess->NbPending += 1;
326
0
    com->is_resend = GF_FALSE;
327
0
  }
328
329
0
  if (!strcmp(com->method, GF_RTSP_OPTIONS)) {
330
0
    sprintf(buffer, "OPTIONS %s %s\r\n", sCtrl, GF_RTSP_VERSION);
331
0
  } else {
332
0
    rad = (sess->ConnectionType == GF_SOCK_TYPE_TCP) ? "rtsp" : "rtspu";
333
0
    if (sCtrl) {
334
0
      char szServURL[1024];
335
0
      sprintf(szServURL, "%s://%s:%d/%s", rad, sess->Server, sess->Port, sess->Service);
336
0
      char *ctrl_url = gf_url_concatenate(szServURL, sCtrl);
337
0
      sprintf(buffer, "%s %s %s\r\n", com->method, ctrl_url, GF_RTSP_VERSION);
338
0
      gf_free(ctrl_url);
339
0
    } else {
340
0
      sprintf(buffer, "%s %s://%s:%d/%s %s\r\n", com->method, rad, sess->Server, sess->Port, sess->Service, GF_RTSP_VERSION);
341
0
    }
342
0
  }
343
344
  //Body on ANNOUNCE, GET_PARAMETER, SET_PARAMETER ONLY
345
0
  body = NULL;
346
0
  if (strcmp(com->method, GF_RTSP_ANNOUNCE)
347
0
          && strcmp(com->method, GF_RTSP_GET_PARAMETER)
348
0
          && strcmp(com->method, GF_RTSP_SET_PARAMETER)
349
0
     ) {
350
    //this is an error, but don't say anything
351
0
    if (com->body) {
352
0
      body = com->body;
353
0
      com->body = NULL;
354
0
    }
355
0
  }
356
357
0
  result = NULL;
358
0
  e = RTSP_WriteCommand(sess, com, (unsigned char *)buffer, (unsigned char **) &result, &size);
359
  //restore body if needed
360
0
  if (body) com->body = body;
361
0
  if (e) goto exit;
362
363
364
0
  GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Sending Command:\n%s\n", result));
365
366
  //send buffer
367
0
  e = gf_rtsp_send_data(sess, result, size);
368
0
  if (e)
369
0
    goto exit;
370
371
372
  //update our state
373
0
  if (!strcmp(com->method, GF_RTSP_RECORD)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL;
374
0
  else if (!strcmp(com->method, GF_RTSP_PLAY)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL;
375
0
  else if (!strcmp(com->method, GF_RTSP_PAUSE)) sess->RTSP_State = GF_RTSP_STATE_WAIT_FOR_CONTROL;
376
0
  else sess->RTSP_State = GF_RTSP_STATE_WAITING;
377
  //teardown invalidates the session most of the time, so we force the user to wait for the reply
378
  //as the reply may indicate a connection-closed
379
0
  strcpy(sess->RTSPLastRequest, com->method);
380
381
0
exit:
382
0
  if (result) gf_free(result);
383
0
  return e;
384
0
}
385
386
387
void gf_rtsp_set_command_value(GF_RTSPCommand *com, char *Header, char *Value)
388
0
{
389
0
  char LineBuffer[400];
390
0
  s32 LinePos;
391
0
  GF_RTSPTransport *trans;
392
0
  GF_X_Attribute *x_Att;
393
394
0
  if (!stricmp(Header, "Accept")) com->Accept = gf_strdup(Value);
395
0
  else if (!stricmp(Header, "Accept-Encoding")) com->Accept_Encoding = gf_strdup(Value);
396
0
  else if (!stricmp(Header, "Accept-Language")) com->Accept_Language = gf_strdup(Value);
397
0
  else if (!stricmp(Header, "Authorization")) com->Authorization = gf_strdup(Value);
398
0
  else if (!stricmp(Header, "Bandwidth")) sscanf(Value, "%u", &com->Bandwidth);
399
0
  else if (!stricmp(Header, "Blocksize")) sscanf(Value, "%u", &com->Blocksize);
400
0
  else if (!stricmp(Header, "Cache-Control")) com->Cache_Control = gf_strdup(Value);
401
0
  else if (!stricmp(Header, "Conference")) com->Conference = gf_strdup(Value);
402
0
  else if (!stricmp(Header, "Connection")) com->Connection = gf_strdup(Value);
403
0
  else if (!stricmp(Header, "Content-Length")) sscanf(Value, "%u", &com->Content_Length);
404
0
  else if (!stricmp(Header, "CSeq")) sscanf(Value, "%u", &com->CSeq);
405
0
  else if (!stricmp(Header, "From")) com->From = gf_strdup(Value);
406
0
  else if (!stricmp(Header, "Proxy_Authorization")) com->Proxy_Authorization = gf_strdup(Value);
407
0
  else if (!stricmp(Header, "Proxy_Require")) com->Proxy_Require = gf_strdup(Value);
408
0
  else if (!stricmp(Header, "Range")) com->Range = gf_rtsp_range_parse(Value);
409
0
  else if (!stricmp(Header, "Referer")) com->Referer = gf_strdup(Value);
410
0
  else if (!stricmp(Header, "Scale")) sscanf(Value, "%lf", &com->Scale);
411
0
  else if (!stricmp(Header, "Session"))
412
0
    com->Session = gf_strdup(Value);
413
0
  else if (!stricmp(Header, "Speed")) sscanf(Value, "%lf", &com->Speed);
414
0
  else if (!stricmp(Header, "User_Agent")) com->User_Agent = gf_strdup(Value);
415
  //Transports
416
0
  else if (!stricmp(Header, "Transport")) {
417
0
    LinePos = 0;
418
0
    while (1) {
419
0
      LinePos = gf_token_get(Value, LinePos, "\r\n", LineBuffer, 400);
420
0
      if (LinePos <= 0) return;
421
0
      trans = gf_rtsp_transport_parse(Value);
422
0
      if (trans) gf_list_add(com->Transports, trans);
423
0
    }
424
0
  }
425
  //eXtensions attributes
426
0
  else if (!strnicmp(Header, "x-", 2)) {
427
0
    x_Att = (GF_X_Attribute*)gf_malloc(sizeof(GF_X_Attribute));
428
0
    x_Att->Name = gf_strdup(Header+2);
429
0
    x_Att->Value = NULL;
430
0
    if (Value && strlen(Value)) x_Att->Value = gf_strdup(Value);
431
0
    gf_list_add(com->Xtensions, x_Att);
432
0
  }
433
  //the rest is ignored
434
0
}
435
436
GF_Err RTSP_ParseCommandHeader(GF_RTSPSession *sess, GF_RTSPCommand *com, u32 BodyStart)
437
0
{
438
0
  char LineBuffer[1024];
439
0
  char ValBuf[1024];
440
0
  char *buffer;
441
0
  s32 Pos, ret;
442
0
  u32 Size;
443
444
0
  Size = sess->CurrentSize - sess->CurrentPos;
445
0
  buffer = sess->tcp_buffer + sess->CurrentPos;
446
447
  //by default the command is wrong ;)
448
0
  com->StatusCode = NC_RTSP_Bad_Request;
449
450
  //parse first line
451
0
  ret = gf_token_get_line(buffer, 0, Size, LineBuffer, 1024);
452
0
  if (ret < 0) return GF_REMOTE_SERVICE_ERROR;
453
454
  //method
455
0
  Pos = gf_token_get(LineBuffer, 0, " \t\r\n", ValBuf, 1024);
456
0
  if (Pos <= 0) return GF_OK;
457
0
  com->method = gf_strdup((const char *) ValBuf);
458
459
  //URL
460
0
  Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 1024);
461
0
  if (Pos <= 0) return GF_OK;
462
0
  com->service_name = gf_strdup(ValBuf);
463
464
  //RTSP version
465
0
  Pos = gf_token_get(LineBuffer, Pos, " \t\r\n", ValBuf, 1024);
466
0
  if (Pos <= 0) return GF_OK;
467
0
  if (strcmp(ValBuf, GF_RTSP_VERSION)) {
468
0
    com->StatusCode = NC_RTSP_RTSP_Version_Not_Supported;
469
0
    return GF_OK;
470
0
  }
471
472
0
  com->StatusCode = NC_RTSP_OK;
473
474
0
  return gf_rtsp_parse_header(buffer + ret, Size - ret, BodyStart, com, NULL);
475
0
}
476
477
char *RTSP_DEFINED_METHODS[] =
478
{
479
  GF_RTSP_DESCRIBE,
480
  GF_RTSP_SETUP,
481
  GF_RTSP_PLAY,
482
  GF_RTSP_PAUSE,
483
  GF_RTSP_RECORD,
484
  GF_RTSP_TEARDOWN,
485
  GF_RTSP_GET_PARAMETER,
486
  GF_RTSP_SET_PARAMETER,
487
  GF_RTSP_OPTIONS,
488
  GF_RTSP_ANNOUNCE,
489
  GF_RTSP_REDIRECT,
490
  NULL
491
};
492
493
GF_EXPORT
494
GF_Err gf_rtsp_get_command(GF_RTSPSession *sess, GF_RTSPCommand *com)
495
0
{
496
0
  GF_Err e;
497
0
  u32 BodyStart, size, com_buf_size;
498
0
  if (!sess || !com) return GF_BAD_PARAM;
499
500
  //reset the command
501
0
  gf_rtsp_command_reset(com);
502
  //if no connection, we have sent a "Connection: Close"
503
0
  if (!sess->connection) return GF_IP_CONNECTION_CLOSED;
504
505
  //lock
506
  //fill TCP buffer
507
0
  e = gf_rtsp_fill_buffer(sess);
508
0
  if (e) goto exit;
509
  //this is upcoming, interleaved data
510
0
  if (sess->TCPChannels && sess->interleaved) {
511
0
    u32 i=0;
512
0
    Bool sync = GF_FALSE;
513
0
    while (RTSP_DEFINED_METHODS[i]) {
514
0
      if (!strncmp(sess->tcp_buffer+sess->CurrentPos, RTSP_DEFINED_METHODS[i], strlen(RTSP_DEFINED_METHODS[i]) ) ) {
515
0
        sync = GF_TRUE;
516
0
        break;
517
0
      }
518
0
    }
519
0
    if (!sync) {
520
0
      e = GF_IP_NETWORK_EMPTY;
521
0
      goto exit;
522
0
    }
523
0
  }
524
0
  e = gf_rtsp_read_reply(sess);
525
0
  if (e) goto exit;
526
527
0
  gf_rtsp_get_body_info(sess, &BodyStart, &size, GF_FALSE);
528
0
  com_buf_size=0;
529
0
  if (sess->tunnel_mode==RTSP_HTTP_SERVER) {
530
    //no body start, this means no '=' at the end of the base64 string, assume we got the entire message
531
0
    if (!BodyStart) BodyStart = sess->CurrentSize - sess->CurrentPos;
532
0
    com_buf_size = BodyStart;
533
0
    u32 dsize = gf_base64_decode(sess->tcp_buffer+sess->CurrentPos, BodyStart, sess->tcp_buffer+sess->CurrentPos, BodyStart);
534
0
    sess->tcp_buffer[sess->CurrentPos + dsize] = 0;
535
0
    gf_rtsp_get_body_info(sess, &BodyStart, &size, GF_TRUE);
536
0
  }
537
538
0
  GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSP] Got Command:\n%s\n", sess->tcp_buffer+sess->CurrentPos));
539
540
0
  e = RTSP_ParseCommandHeader(sess, com, BodyStart);
541
  //before returning an error we MUST reset the TCP buffer
542
543
  //copy the body if any
544
0
  if (!e && com->Content_Length) {
545
0
    u32 rsp_size = sess->CurrentSize - sess->CurrentPos;
546
0
    if (rsp_size < com->Content_Length) {
547
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTSP] Invalid content length %u - Response was: \n%s\n", com->Content_Length, sess->tcp_buffer+sess->CurrentPos));
548
0
      e = GF_NON_COMPLIANT_BITSTREAM;
549
0
      goto exit;
550
0
    }
551
0
    com->body = (char *) gf_malloc(sizeof(char) * (com->Content_Length));
552
0
    memcpy(com->body, sess->tcp_buffer+sess->CurrentPos + BodyStart, com->Content_Length);
553
0
  }
554
  //reset TCP buffer
555
0
  if (!com_buf_size) com_buf_size = BodyStart + com->Content_Length;
556
0
  sess->CurrentPos += com_buf_size;
557
558
0
  if (!sess->CSeq && !sess->HTTP_Cookie //first request , cookie not set
559
0
    && (sess->tunnel_mode!=RTSP_HTTP_DISABLE) //not disabled
560
0
    && (!strncmp(sess->tcp_buffer, "GET ", 4) || !strncmp(sess->tcp_buffer, "POST ", 5))
561
0
  ) {
562
0
    char *sess_cookie = strstr(sess->tcp_buffer, "x-sessioncookie");
563
0
    if (sess_cookie) sess_cookie = strchr(sess_cookie, ':');
564
0
    if (sess_cookie) {
565
0
      while (strchr(" :", sess_cookie[0]))
566
0
        sess_cookie++;
567
0
      char *sep = strchr(sess_cookie, '\r');
568
0
      if (sep) {
569
0
        sep[0] = 0;
570
0
        sess->HTTP_Cookie = gf_strdup(sess_cookie);
571
0
        sep[0] = '\r';
572
573
0
        if (!strncmp(sess->tcp_buffer, "GET ", 4)) {
574
0
          char szCom[501];
575
0
          snprintf(szCom, 500, "HTTP/1.0 200 OK\r\nServer: %s\r\nCache-Control: no-store\r\nPragma: no-cache\r\nContent-Type: application/x-rtsp-tunnelled\r\n\r\n", sess->Server);
576
0
          szCom[500] = 0;
577
578
0
          GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTSPTunnel] Send reply %s\n", szCom));
579
580
0
          e = gf_rtsp_send_data(sess, szCom, (u32) strlen(szCom));
581
0
          if (e && (e!= GF_IP_NETWORK_EMPTY)) return e;
582
0
          return GF_IP_NETWORK_EMPTY;
583
0
        }
584
        //post
585
0
        return GF_RTSP_TUNNEL_POST;
586
0
      }
587
0
    }
588
0
  }
589
590
591
0
  if (!com->CSeq)
592
0
    com->StatusCode = NC_RTSP_Bad_Request;
593
594
0
  if (e || (com->StatusCode != NC_RTSP_OK)) goto exit;
595
596
  //NB: there is no "session state" in our lib when acting at the server side, as it depends
597
  //on the server implementation. We cannot block responses / announcement to be sent
598
  //dynamically, nor reset the session ourselves as we don't know the details of the session
599
  //(eg TEARDOWN may keep resources up or not, ...)
600
601
  //we also have the same pb for CSeq, as nothing forbids a server to buffer commands (and it
602
  //happens during aggregation of PLAY/PAUSE with overlapping ranges)
603
604
  //however store the last CSeq in case for client checking
605
0
  if (!sess->CSeq) {
606
0
    sess->CSeq = com->CSeq;
607
0
  }
608
  //check we're in the right range
609
0
  else {
610
0
    if (sess->CSeq >= com->CSeq)
611
0
      com->StatusCode = NC_RTSP_Header_Field_Not_Valid;
612
0
    else
613
0
      sess->CSeq = com->CSeq;
614
0
  }
615
616
  //
617
  //if a connection closed is signal, check this is the good session
618
  // and reset it (the client is no longer connected)
619
0
  if (sess->last_session_id && com->Session && !strcmp(com->Session, sess->last_session_id)
620
0
          && com->Connection && !stricmp(com->Connection, "Close")) {
621
622
0
    gf_rtsp_session_reset(sess, GF_FALSE);
623
    //destroy the socket
624
0
    if (sess->connection) gf_sk_del(sess->connection);
625
0
    sess->connection = NULL;
626
627
    //destroy the http tunnel if any
628
0
    if (sess->http) {
629
0
      gf_sk_del(sess->http);
630
0
      sess->http = NULL;
631
0
    }
632
0
  }
633
634
0
exit:
635
0
  return e;
636
0
}
637
638
#endif /*GPAC_DISABLE_STREAMING*/