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