1 /*
2  *  Copyright (C) 2009 Andrej Stepanchuk
3  *  Copyright (C) 2009-2010 Howard Chu
4  *
5  *  This file is part of librtmp.
6  *
7  *  librtmp is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as
9  *  published by the Free Software Foundation; either version 2.1,
10  *  or (at your option) any later version.
11  *
12  *  librtmp is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with librtmp see the file COPYING.  If not, write to
19  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA  02110-1301, USA.
21  *  http://www.gnu.org/copyleft/lgpl.html
22  */
23 
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <assert.h>
28 #include <ctype.h>
29 
30 #include "rtmp_sys.h"
31 #include "log.h"
32 
RTMP_ParseURL(const char * url,int * protocol,AVal * host,unsigned int * port,AVal * playpath,AVal * app)33 int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port,
34 	AVal *playpath, AVal *app)
35 {
36 	char *p, *end, *col, *ques, *slash;
37 
38 	RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
39 
40 	*protocol = RTMP_PROTOCOL_RTMP;
41 	*port = 0;
42 	playpath->av_len = 0;
43 	playpath->av_val = NULL;
44 	app->av_len = 0;
45 	app->av_val = NULL;
46 
47 	/* Old School Parsing */
48 
49 	/* look for usual :// pattern */
50 	p = strstr(url, "://");
51 	if(!p) {
52 		RTMP_Log(RTMP_LOGERROR, "RTMP URL: No :// in url!");
53 		return FALSE;
54 	}
55 	{
56 	int len = (int)(p-url);
57 
58 	if(len == 4 && strncasecmp(url, "rtmp", 4)==0)
59 		*protocol = RTMP_PROTOCOL_RTMP;
60 	else if(len == 5 && strncasecmp(url, "rtmpt", 5)==0)
61 		*protocol = RTMP_PROTOCOL_RTMPT;
62 	else if(len == 5 && strncasecmp(url, "rtmps", 5)==0)
63 	        *protocol = RTMP_PROTOCOL_RTMPS;
64 	else if(len == 5 && strncasecmp(url, "rtmpe", 5)==0)
65 	        *protocol = RTMP_PROTOCOL_RTMPE;
66 	else if(len == 5 && strncasecmp(url, "rtmfp", 5)==0)
67 	        *protocol = RTMP_PROTOCOL_RTMFP;
68 	else if(len == 6 && strncasecmp(url, "rtmpte", 6)==0)
69 	        *protocol = RTMP_PROTOCOL_RTMPTE;
70 	else if(len == 6 && strncasecmp(url, "rtmpts", 6)==0)
71 	        *protocol = RTMP_PROTOCOL_RTMPTS;
72 	else {
73 		RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n");
74 		goto parsehost;
75 	}
76 	}
77 
78 	RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol);
79 
80 parsehost:
81 	/* let's get the hostname */
82 	p+=3;
83 
84 	/* check for sudden death */
85 	if(*p==0) {
86 		RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!");
87 		return FALSE;
88 	}
89 
90 	end   = p + strlen(p);
91 	col   = strchr(p, ':');
92 	ques  = strchr(p, '?');
93 	slash = strchr(p, '/');
94 
95 	{
96 	int hostlen;
97 	if(slash)
98 		hostlen = slash - p;
99 	else
100 		hostlen = end - p;
101 	if(col && col -p < hostlen)
102 		hostlen = col - p;
103 
104 	if(hostlen < 256) {
105 		host->av_val = p;
106 		host->av_len = hostlen;
107 		RTMP_Log(RTMP_LOGDEBUG, "Parsed host    : %.*s", hostlen, host->av_val);
108 	} else {
109 		RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!");
110 	}
111 
112 	p+=hostlen;
113 	}
114 
115 	/* get the port number if available */
116 	if(*p == ':') {
117 		unsigned int p2;
118 		p++;
119 		p2 = atoi(p);
120 		if(p2 > 65535) {
121 			RTMP_Log(RTMP_LOGWARNING, "Invalid port number!");
122 		} else {
123 			*port = p2;
124 		}
125 	}
126 
127 	if(!slash) {
128 		RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!");
129 		return TRUE;
130 	}
131 	p = slash+1;
132 
133 	{
134 	/* parse application
135 	 *
136 	 * rtmp://host[:port]/app[/appinstance][/...]
137 	 * application = app[/appinstance]
138 	 */
139 
140 	char *slash2, *slash3 = NULL, *slash4 = NULL;
141 	int applen, appnamelen;
142 
143 	slash2 = strchr(p, '/');
144 	if(slash2)
145 		slash3 = strchr(slash2+1, '/');
146 	if(slash3)
147 		slash4 = strchr(slash3+1, '/');
148 
149 	applen = end-p; /* ondemand, pass all parameters as app */
150 	appnamelen = applen; /* ondemand length */
151 
152 	if(ques && strstr(p, "slist=")) { /* whatever it is, the '?' and slist= means we need to use everything as app and parse plapath from slist= */
153 		appnamelen = ques-p;
154 	}
155 	else if(strncmp(p, "ondemand/", 9)==0) {
156                 /* app = ondemand/foobar, only pass app=ondemand */
157                 applen = 8;
158                 appnamelen = 8;
159         }
160 	else { /* app!=ondemand, so app is app[/appinstance] */
161 		if(slash4)
162 			appnamelen = slash4-p;
163 		else if(slash3)
164 			appnamelen = slash3-p;
165 		else if(slash2)
166 			appnamelen = slash2-p;
167 
168 		applen = appnamelen;
169 	}
170 
171 	app->av_val = p;
172 	app->av_len = applen;
173 	RTMP_Log(RTMP_LOGDEBUG, "Parsed app     : %.*s", applen, p);
174 
175 	p += appnamelen;
176 	}
177 
178 	if (*p == '/')
179 		p++;
180 
181 	if (end-p) {
182 		AVal av = {p, end-p};
183 		RTMP_ParsePlaypath(&av, playpath);
184 	}
185 
186 	return TRUE;
187 }
188 
189 /*
190  * Extracts playpath from RTMP URL. playpath is the file part of the
191  * URL, i.e. the part that comes after rtmp://host:port/app/
192  *
193  * Returns the stream name in a format understood by FMS. The name is
194  * the playpath part of the URL with formatting depending on the stream
195  * type:
196  *
197  * mp4 streams: prepend "mp4:", remove extension
198  * mp3 streams: prepend "mp3:", remove extension
199  * flv streams: remove extension
200  */
RTMP_ParsePlaypath(AVal * in,AVal * out)201 void RTMP_ParsePlaypath(AVal *in, AVal *out) {
202 	int addMP4 = 0;
203 	int addMP3 = 0;
204 	int subExt = 0;
205 	const char *playpath = in->av_val;
206 	const char *temp, *q, *ext = NULL;
207 	const char *ppstart = playpath;
208 	char *streamname, *destptr, *p;
209 
210 	int pplen = in->av_len;
211 
212 	out->av_val = NULL;
213 	out->av_len = 0;
214 
215 	if ((*ppstart == '?') &&
216 	    (temp=strstr(ppstart, "slist=")) != 0) {
217 		ppstart = temp+6;
218 		pplen = strlen(ppstart);
219 
220 		temp = strchr(ppstart, '&');
221 		if (temp) {
222 			pplen = temp-ppstart;
223 		}
224 	}
225 
226 	q = strchr(ppstart, '?');
227 	if (pplen >= 4) {
228 		if (q)
229 			ext = q-4;
230 		else
231 			ext = &ppstart[pplen-4];
232 		if ((strncmp(ext, ".f4v", 4) == 0) ||
233 		    (strncmp(ext, ".mp4", 4) == 0)) {
234 			addMP4 = 1;
235 			subExt = 1;
236 		/* Only remove .flv from rtmp URL, not slist params */
237 		} else if ((ppstart == playpath) &&
238 		    (strncmp(ext, ".flv", 4) == 0)) {
239 			subExt = 1;
240 		} else if (strncmp(ext, ".mp3", 4) == 0) {
241 			addMP3 = 1;
242 			subExt = 1;
243 		}
244 	}
245 
246 	streamname = (char *)malloc((pplen+4+1)*sizeof(char));
247 	if (!streamname)
248 		return;
249 
250 	destptr = streamname;
251 	if (addMP4) {
252 		if (strncmp(ppstart, "mp4:", 4)) {
253 			strcpy(destptr, "mp4:");
254 			destptr += 4;
255 		} else {
256 			subExt = 0;
257 		}
258 	} else if (addMP3) {
259 		if (strncmp(ppstart, "mp3:", 4)) {
260 			strcpy(destptr, "mp3:");
261 			destptr += 4;
262 		} else {
263 			subExt = 0;
264 		}
265 	}
266 
267  	for (p=(char *)ppstart; pplen >0;) {
268 		/* skip extension */
269 		if (subExt && p == ext) {
270 			p += 4;
271 			pplen -= 4;
272 			continue;
273 		}
274 		if (*p == '%') {
275 			unsigned int c;
276 			sscanf(p+1, "%02x", &c);
277 			*destptr++ = c;
278 			pplen -= 3;
279 			p += 3;
280 		} else {
281 			*destptr++ = *p++;
282 			pplen--;
283 		}
284 	}
285 	*destptr = '\0';
286 
287 	out->av_val = streamname;
288 	out->av_len = destptr - streamname;
289 }
290