Look for a fourth slash when splitting the url into app+playpath
[rtmpdump.git] / librtmp / parseurl.c
blob646c70c188285312a07eaf40a81edbfc8fbc3704
1 /*
2 * Copyright (C) 2009 Andrej Stepanchuk
3 * Copyright (C) 2009-2010 Howard Chu
5 * This file is part of librtmp.
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.
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.
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
24 #include <stdlib.h>
25 #include <string.h>
27 #include <assert.h>
28 #include <ctype.h>
30 #include "rtmp_sys.h"
31 #include "log.h"
33 int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port,
34 AVal *playpath, AVal *app)
36 char *p, *end, *col, *ques, *slash;
38 RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
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;
47 /* Old School Parsing */
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;
56 int len = (int)(p-url);
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;
78 RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol);
80 parsehost:
81 /* let's get the hostname */
82 p+=3;
84 /* check for sudden death */
85 if(*p==0) {
86 RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!");
87 return FALSE;
90 end = p + strlen(p);
91 col = strchr(p, ':');
92 ques = strchr(p, '?');
93 slash = strchr(p, '/');
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;
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!");
112 p+=hostlen;
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;
127 if(!slash) {
128 RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!");
129 return TRUE;
131 p = slash+1;
134 /* parse application
136 * rtmp://host[:port]/app[/appinstance][/...]
137 * application = app[/appinstance]
140 char *slash2, *slash3 = NULL, *slash4 = NULL;
141 int applen, appnamelen;
143 slash2 = strchr(p, '/');
144 if(slash2)
145 slash3 = strchr(slash2+1, '/');
146 if(slash3)
147 slash4 = strchr(slash3+1, '/');
149 applen = end-p; /* ondemand, pass all parameters as app */
150 appnamelen = applen; /* ondemand length */
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;
155 else if(strncmp(p, "ondemand/", 9)==0) {
156 /* app = ondemand/foobar, only pass app=ondemand */
157 applen = 8;
158 appnamelen = 8;
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;
168 applen = appnamelen;
171 app->av_val = p;
172 app->av_len = applen;
173 RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p);
175 p += appnamelen;
178 if (*p == '/')
179 p++;
181 if (end-p) {
182 AVal av = {p, end-p};
183 RTMP_ParsePlaypath(&av, playpath);
186 return TRUE;
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/
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:
197 * mp4 streams: prepend "mp4:", remove extension
198 * mp3 streams: prepend "mp3:", remove extension
199 * flv streams: remove extension
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;
210 int pplen = in->av_len;
212 out->av_val = NULL;
213 out->av_len = 0;
215 if ((*ppstart == '?') &&
216 (temp=strstr(ppstart, "slist=")) != 0) {
217 ppstart = temp+6;
218 pplen = strlen(ppstart);
220 temp = strchr(ppstart, '&');
221 if (temp) {
222 pplen = temp-ppstart;
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;
246 streamname = (char *)malloc((pplen+4+1)*sizeof(char));
247 if (!streamname)
248 return;
250 destptr = streamname;
251 if (addMP4) {
252 if (strncmp(ppstart, "mp4:", 4)) {
253 strcpy(destptr, "mp4:");
254 destptr += 4;
255 } else {
256 subExt = 0;
258 } else if (addMP3) {
259 if (strncmp(ppstart, "mp3:", 4)) {
260 strcpy(destptr, "mp3:");
261 destptr += 4;
262 } else {
263 subExt = 0;
267 for (p=(char *)ppstart; pplen >0;) {
268 /* skip extension */
269 if (subExt && p == ext) {
270 p += 4;
271 pplen -= 4;
272 continue;
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--;
285 *destptr = '\0';
287 out->av_val = streamname;
288 out->av_len = destptr - streamname;