]> git.ozlabs.org Git - petitboot/blob - lib/url/url.c
ae72b103de62153e2a8e779286c7a8d3a01d2a55
[petitboot] / lib / url / url.c
1 /*
2  *  Copyright (C) 2009 Sony Computer Entertainment Inc.
3  *  Copyright 2009 Sony Corp.
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; version 2 of the License.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 #if defined(HAVE_CONFIG_H)
20 #include "config.h"
21 #endif
22
23 #define _GNU_SOURCE
24 #include <assert.h>
25 #include <stdbool.h>
26 #include <string.h>
27
28 #include "log/log.h"
29 #include "talloc/talloc.h"
30 #include "url.h"
31
32 /**
33  * pb_scheme_info - Helper for parsing URLs.
34  */
35
36 struct pb_scheme_info {
37         enum pb_url_scheme scheme;
38         const char *str;
39         unsigned int str_len;
40         bool has_host;
41 };
42
43 static const struct pb_scheme_info schemes[] = {
44         {
45                 .scheme = pb_url_file,
46                 .str = "file",
47                 .str_len = sizeof("file") - 1,
48                 .has_host = false,
49         },
50         {
51                 .scheme = pb_url_ftp,
52                 .str = "ftp",
53                 .str_len = sizeof("ftp") - 1,
54                 .has_host = true,
55         },
56         {
57                 .scheme = pb_url_http,
58                 .str = "http",
59                 .str_len = sizeof("http") - 1,
60                 .has_host = true,
61         },
62         {
63                 .scheme = pb_url_https,
64                 .str = "https",
65                 .str_len = sizeof("https") - 1,
66                 .has_host = true,
67         },
68         {
69                 .scheme = pb_url_nfs,
70                 .str = "nfs",
71                 .str_len = sizeof("nfs") - 1,
72                 .has_host = true,
73         },
74         {
75                 .scheme = pb_url_sftp,
76                 .str = "sftp",
77                 .str_len = sizeof("sftp") - 1,
78                 .has_host = true,
79         },
80         {
81                 .scheme = pb_url_tftp,
82                 .str = "tftp",
83                 .str_len = sizeof("tftp") - 1,
84                 .has_host = true,
85         },
86 };
87
88 static const struct pb_scheme_info *file_scheme = &schemes[0];
89
90 /**
91  * pb_url_find_scheme - Find the pb_scheme_info for a URL string.
92  */
93
94 static const struct pb_scheme_info *pb_url_scheme_info(
95                 enum pb_url_scheme scheme)
96 {
97         unsigned int i;
98
99         for (i = 0; i < sizeof(schemes) / sizeof(schemes[0]); i++) {
100                 const struct pb_scheme_info *info = &schemes[i];
101
102                 if (info->scheme == scheme)
103                         return info;
104
105         }
106         return NULL;
107 }
108
109 static const struct pb_scheme_info *pb_url_find_scheme(const char *url)
110 {
111         static const int sep_len = sizeof("://") - 1;
112         static const char *sep = "://";
113         unsigned int i, url_len;
114
115         url_len = strlen(url);
116
117         for (i = 0; i < sizeof(schemes) / sizeof(schemes[0]); i++) {
118                 const struct pb_scheme_info *scheme = &schemes[i];
119
120                 if (url_len < scheme->str_len + sep_len)
121                         continue;
122
123                 if (strncmp(url + scheme->str_len, sep, sep_len))
124                         continue;
125
126                 if (strncasecmp(url, scheme->str, scheme->str_len))
127                         continue;
128
129                 return scheme;
130         }
131
132         /* Assume this is a non-url local file. */
133
134         return file_scheme;
135 }
136
137 static void pb_url_parse_path(struct pb_url *url)
138 {
139         const char *p = strrchr(url->path, '/');
140
141         talloc_free(url->dir);
142         talloc_free(url->file);
143
144         if (p) {
145                 p++;
146                 url->dir = talloc_strndup(url, url->path, p - url->path);
147                 url->file = talloc_strdup(url, p);
148         } else {
149                 url->dir = NULL;
150                 url->file = talloc_strdup(url, url->path);
151         }
152 }
153
154 /**
155  * pb_url_parse - Parse a remote file URL.
156  * @ctx: The talloc context to associate with the returned string.
157  *
158  * Returns a talloc'ed struct pb_url instance on success, or NULL on error.
159  */
160
161 struct pb_url *pb_url_parse(void *ctx, const char *url_str)
162 {
163         const struct pb_scheme_info *si;
164         struct pb_url *url;
165         const char *p;
166
167         pb_log("%s: '%s'\n", __func__, url_str);
168
169         if (!url_str || !*url_str) {
170                 assert(0 && "bad url");
171                 return NULL;
172         }
173
174         url = talloc_zero(ctx, struct pb_url);
175
176         if (!url)
177                 return NULL;
178
179         si = pb_url_find_scheme(url_str);
180
181         url->scheme = si->scheme;
182         p = url_str + si->str_len + strlen("://");
183
184         url->full = talloc_strdup(url, url_str);
185
186         if (url->scheme == pb_url_file) {
187                 url->port = NULL;
188                 url->host = NULL;
189                 url->path = talloc_strdup(url, p);
190         } else {
191                 int len;
192                 const char *col;
193                 const char *path;
194
195                 path = strchr(p, '/');
196
197                 if (!path) {
198                         pb_log("%s: parse path failed '%s'\n", __func__ , p);
199                         goto fail;
200                 }
201
202                 col = strchr(p, ':');
203
204                 if (col) {
205                         len = path - col - 1;
206                         url->port = len ? talloc_strndup(url, col + 1, len)
207                                 : NULL;
208                         len = col - p;
209                         url->host = len ? talloc_strndup(url, p, len) : NULL;
210                 } else {
211                         url->port = NULL;
212                         url->host = talloc_strndup(url, p, path - p);
213                 }
214
215                 /* remove multiple leading slashes */
216                 for (; *path && *(path+1) == '/'; path++)
217                         ;
218
219                 url->path = talloc_strdup(url, path);
220         }
221
222         pb_url_parse_path(url);
223
224         pb_log(" scheme %d\n", url->scheme);
225         pb_log(" host '%s'\n", url->host);
226         pb_log(" port '%s'\n", url->port);
227         pb_log(" path '%s'\n", url->path);
228         pb_log(" dir '%s'\n", url->dir);
229         pb_log(" file '%s'\n", url->file);
230
231         return url;
232
233 fail:
234         talloc_free(url);
235         return NULL;
236 }
237
238 static bool is_url(const char *str)
239 {
240         return strstr(str, "://") != NULL;
241 }
242
243 static void pb_url_update_full(struct pb_url *url)
244 {
245         const struct pb_scheme_info *scheme = pb_url_scheme_info(url->scheme);
246
247         assert(scheme);
248
249         talloc_free(url->full);
250
251         url->full = talloc_asprintf(url, "%s://%s%s", scheme->str,
252                         scheme->has_host ? url->host : "", url->path);
253 }
254
255 static struct pb_url *pb_url_copy(void *ctx, const struct pb_url *url)
256 {
257         struct pb_url *new_url;
258
259         new_url = talloc(ctx, struct pb_url);
260         new_url->scheme = url->scheme;
261         new_url->full = talloc_strdup(url, url->full);
262
263         new_url->host = url->host ? talloc_strdup(url, url->host) : NULL;
264         new_url->port = url->port ? talloc_strdup(url, url->port) : NULL;
265         new_url->path = url->path ? talloc_strdup(url, url->path) : NULL;
266         new_url->dir  = url->dir  ? talloc_strdup(url, url->dir)  : NULL;
267         new_url->file = url->file ? talloc_strdup(url, url->file) : NULL;
268
269         return new_url;
270 }
271
272 struct pb_url *pb_url_join(void *ctx, const struct pb_url *url, const char *s)
273 {
274         struct pb_url *new_url;
275
276         /* complete url: just parse all info from s */
277         if (is_url(s))
278                 return pb_url_parse(ctx, s);
279
280         new_url = pb_url_copy(ctx, url);
281
282         if (s[0] == '/') {
283                 /* absolute path: replace path of new_url */
284                 talloc_free(new_url->path);
285                 new_url->path = talloc_strdup(new_url, s);
286
287         } else {
288                 /* relative path: join s to existing path. We know that
289                  * url->dir ends with a slash. */
290                 char *tmp = new_url->path;
291                 new_url->path = talloc_asprintf(new_url, "%s%s", url->dir, s);
292                 talloc_free(tmp);
293         }
294
295         /* replace ->dir and ->file with components from ->path */
296         pb_url_parse_path(new_url);
297
298         /* and re-generate the full URL */
299         pb_url_update_full(new_url);
300
301         return new_url;
302 }
303
304 const char *pb_url_scheme_name(enum pb_url_scheme scheme)
305 {
306         const struct pb_scheme_info *info = pb_url_scheme_info(scheme);
307         return info ? info->str : NULL;
308 }