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