1 /*
2  *  Tvheadend - URL Processing
3  *
4  *  Copyright (C) 2013 Adam Sutton
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "tvheadend.h"
21 #include "url.h"
22 
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <regex.h>
26 #include <string.h>
27 
28 
29 void
urlreset(url_t * url)30 urlreset ( url_t *url )
31 {
32   free(url->scheme);
33   free(url->user);
34   free(url->pass);
35   free(url->host);
36   free(url->path);
37   free(url->query);
38   free(url->frag);
39   free(url->raw);
40   memset(url, 0, sizeof(*url));
41 }
42 
43 void
urlcopy(url_t * dst,const url_t * src)44 urlcopy ( url_t *dst, const url_t *src )
45 {
46   dst->scheme = src->scheme ? strdup(src->scheme) : NULL;
47   dst->user   = src->user ? strdup(src->user) : NULL;
48   dst->pass   = src->pass ? strdup(src->pass) : NULL;
49   dst->host   = src->host ? strdup(src->host) : NULL;
50   dst->port   = src->port;
51   dst->path   = src->path ? strdup(src->path) : NULL;
52   dst->query  = src->query ? strdup(src->query) : NULL;
53   dst->frag   = src->frag ? strdup(src->frag) : NULL;
54   dst->raw    = src->raw ? strdup(src->raw) : NULL;
55 }
56 
57 /* Use liburiparser if available */
58 #if ENABLE_URIPARSER
59 #include <uriparser/Uri.h>
60 
61 int
urlparse(const char * str,url_t * url)62 urlparse ( const char *str, url_t *url )
63 {
64   UriParserStateA state;
65   UriPathSegmentA *path;
66   UriUriA uri;
67   char *s, buf[256];
68 
69   if (str == NULL || url == NULL)
70     return -1;
71 
72   urlreset(url);
73 
74   /* Parse */
75   state.uri = &uri;
76   if (uriParseUriA(&state, str) != URI_SUCCESS) {
77     uriFreeUriMembersA(&uri);
78     return -1;
79   }
80 
81   /* Store raw */
82   url->raw = strdup(str);
83 
84   /* Copy */
85 #define uri_copy(y, x)\
86   if (x.first) {\
87     size_t len = x.afterLast - x.first;\
88     y = strndup(x.first, len);\
89   }
90 #define uri_copy_static(y, s, x)\
91   if (x.first) {\
92     size_t len = x.afterLast - x.first;\
93     if (len > sizeof(y) - 1) \
94     { y[0] = '\0'; s = strndup(x.first, len); } else \
95     { s = NULL; strlcpy(y, x.first, len + 1); }\
96   } else {\
97     s = NULL;\
98     y[0] = '\0';\
99   }
100   uri_copy(url->scheme, uri.scheme);
101   uri_copy(url->host,   uri.hostText);
102   uri_copy(url->user,   uri.userInfo);
103   uri_copy(url->query,  uri.query);
104   uri_copy(url->frag,   uri.fragment);
105   uri_copy_static(buf, s, uri.portText);
106   if (s) {
107     url->port = atoi(s);
108     free(s);
109   } else if (*buf)
110     url->port = atoi(buf);
111   else
112     url->port = 0;
113   path       = uri.pathHead;
114   while (path) {
115     uri_copy_static(buf, s, path->text);
116     if (url->path)
117       url->path = realloc(url->path, strlen(url->path) + strlen(s ?: buf) + 2);
118     else
119       url->path = calloc(1, strlen(s ?: buf) + 2);
120     strcat(url->path, "/");
121     strcat(url->path, s ?: buf);
122     path = path->next;
123     free(s);
124   }
125   // TODO: query/fragment
126 
127   /* Split user/pass */
128   if (url->user) {
129     s = strstr(url->user, ":");
130     if (s) {
131       url->pass = strdup(s + 1);
132       *s = 0;
133     }
134   }
135 
136   /* Cleanup */
137   uriFreeUriMembersA(&uri);
138   return 0;
139 }
140 
141 void
urlparse_done(void)142 urlparse_done( void )
143 {
144 }
145 
146 /* Fallback to limited support */
147 #else /* ENABLE_URIPARSER */
148 
149 /* URL regexp - I probably found this online */
150 // TODO: does not support ipv6
151 #define UC "[a-z0-9_\\.!£$%^&-]"
152 #define PC UC
153 #define HC "[a-z0-9_\\.-]"
154 #define URL_RE "^([A-Za-z]+)://(("UC"+)(:("PC"+))?@|@)?("HC"+)(:([0-9]+))?(/[^\\?]*)?(.([^#]*))?(#(.*))?"
155 
156 static regex_t *urlparse_exp = NULL;
157 
158 int
urlparse(const char * str,url_t * url)159 urlparse ( const char *str, url_t *url )
160 {
161   regmatch_t m[16];
162   char buf[16];
163 
164   if (str == NULL || url == NULL)
165     return -1;
166 
167   urlreset(url);
168 
169   /* Create regexp */
170   if (!urlparse_exp) {
171     urlparse_exp = calloc(1, sizeof(regex_t));
172     if (regcomp(urlparse_exp, URL_RE, REG_ICASE | REG_EXTENDED)) {
173       tvherror(LS_URL, "failed to compile regexp");
174       exit(1);
175     }
176   }
177 
178   /* Execute */
179   if (regexec(urlparse_exp, str, ARRAY_SIZE(m), m, 0))
180     return -1;
181 
182   /* Extract data */
183 #define copy(x, i)\
184   {\
185     x = strndup(str+m[i].rm_so, m[i].rm_eo - m[i].rm_so);\
186   }(void)0
187 #define copy_static(x, i)\
188   {\
189     int len = m[i].rm_eo - m[i].rm_so;\
190     if (len >= sizeof(x) - 1)\
191       len = sizeof(x) - 1;\
192     memcpy(x, str+m[i].rm_so, len);\
193     x[len] = 0;\
194   }(void)0
195   copy(url->scheme, 1);
196   copy(url->user,   3);
197   copy(url->pass,   5);
198   copy(url->host,   6);
199   copy(url->path,   9);
200   copy_static(buf,  8);
201   url->port = atoi(buf);
202   copy(url->query, 11);
203   copy(url->frag,  13);
204 
205   url->raw = strdup(str);
206 
207   return 0;
208 }
209 
210 void
urlparse_done(void)211 urlparse_done( void )
212 {
213   if (urlparse_exp) {
214     regfree(urlparse_exp);
215     free(urlparse_exp);
216   }
217 }
218 
219 #endif /* ENABLE_URIPARSER */
220