8a3d0db50ee6c38e5f39d8e2eb0ffe8e31928288
[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         return NULL;
133 }
134
135 static void pb_url_parse_path(struct pb_url *url)
136 {
137         const char *p = strrchr(url->path, '/');
138
139         talloc_free(url->dir);
140         talloc_free(url->file);
141
142         if (p) {
143                 p++;
144                 url->dir = talloc_strndup(url, url->path, p - url->path);
145                 url->file = talloc_strdup(url, p);
146         } else {
147                 url->dir = NULL;
148                 url->file = talloc_strdup(url, url->path);
149         }
150 }
151
152 /**
153  * pb_url_parse - Parse a remote file URL.
154  * @ctx: The talloc context to associate with the returned string.
155  *
156  * Returns a talloc'ed struct pb_url instance on success, or NULL on error.
157  */
158
159 struct pb_url *pb_url_parse(void *ctx, const char *url_str)
160 {
161         const struct pb_scheme_info *si;
162         struct pb_url *url;
163         const char *p;
164
165         if (!url_str || !*url_str) {
166                 assert(0 && "bad url");
167                 return NULL;
168         }
169
170         url = talloc_zero(ctx, struct pb_url);
171
172         if (!url)
173                 return NULL;
174
175         si = pb_url_find_scheme(url_str);
176         if (si) {
177                 url->scheme = si->scheme;
178                 p = url_str + si->str_len + strlen("://");
179         } else {
180                 url->scheme = file_scheme->scheme;
181                 p = url_str;
182         }
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         return url;
225
226 fail:
227         talloc_free(url);
228         return NULL;
229 }
230
231 static bool is_url(const char *str)
232 {
233         return strstr(str, "://") != NULL;
234 }
235
236 char *pb_url_to_string(struct pb_url *url)
237 {
238         const struct pb_scheme_info *scheme = pb_url_scheme_info(url->scheme);
239         assert(scheme);
240
241         return talloc_asprintf(url, "%s://%s%s", scheme->str,
242                         scheme->has_host ? url->host : "", url->path);
243 }
244
245 static void pb_url_update_full(struct pb_url *url)
246 {
247         talloc_free(url->full);
248         url->full = pb_url_to_string(url);
249 }
250
251 static struct pb_url *pb_url_copy(void *ctx, const struct pb_url *url)
252 {
253         struct pb_url *new_url;
254
255         new_url = talloc(ctx, struct pb_url);
256         new_url->scheme = url->scheme;
257         new_url->full = talloc_strdup(new_url, url->full);
258
259         new_url->host = url->host ? talloc_strdup(new_url, url->host) : NULL;
260         new_url->port = url->port ? talloc_strdup(new_url, url->port) : NULL;
261         new_url->path = url->path ? talloc_strdup(new_url, url->path) : NULL;
262         new_url->dir  = url->dir  ? talloc_strdup(new_url, url->dir)  : NULL;
263         new_url->file = url->file ? talloc_strdup(new_url, url->file) : NULL;
264
265         return new_url;
266 }
267
268 struct pb_url *pb_url_join(void *ctx, const struct pb_url *url, const char *s)
269 {
270         struct pb_url *new_url;
271
272         /* complete url: just parse all info from s */
273         if (is_url(s))
274                 return pb_url_parse(ctx, s);
275
276         new_url = pb_url_copy(ctx, url);
277
278         if (s[0] == '/') {
279                 /* absolute path: replace path of new_url */
280                 talloc_free(new_url->path);
281                 new_url->path = talloc_strdup(new_url, s);
282
283         } else {
284                 /* relative path: join s to existing path. We know that
285                  * url->dir ends with a slash. */
286                 char *tmp = new_url->path;
287                 new_url->path = talloc_asprintf(new_url, "%s%s", url->dir, s);
288                 talloc_free(tmp);
289         }
290
291         /* replace ->dir and ->file with components from ->path */
292         pb_url_parse_path(new_url);
293
294         /* and re-generate the full URL */
295         pb_url_update_full(new_url);
296
297         return new_url;
298 }
299
300 const char *pb_url_scheme_name(enum pb_url_scheme scheme)
301 {
302         const struct pb_scheme_info *info = pb_url_scheme_info(scheme);
303         return info ? info->str : NULL;
304 }