Add BMC interface MAC to system info output
[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 #include <assert.h>
24 #include <string.h>
25
26 #include "log/log.h"
27 #include "talloc/talloc.h"
28 #include "url.h"
29
30 /**
31  * pb_scheme_info - Helper for parsing URLs.
32  */
33
34 struct pb_scheme_info {
35         enum pb_url_scheme scheme;
36         const char *str;
37         unsigned int str_len;
38         bool has_host;
39 };
40
41 static const struct pb_scheme_info schemes[] = {
42         {
43                 .scheme = pb_url_file,
44                 .str = "file",
45                 .str_len = sizeof("file") - 1,
46                 .has_host = false,
47         },
48         {
49                 .scheme = pb_url_ftp,
50                 .str = "ftp",
51                 .str_len = sizeof("ftp") - 1,
52                 .has_host = true,
53         },
54         {
55                 .scheme = pb_url_http,
56                 .str = "http",
57                 .str_len = sizeof("http") - 1,
58                 .has_host = true,
59         },
60         {
61                 .scheme = pb_url_https,
62                 .str = "https",
63                 .str_len = sizeof("https") - 1,
64                 .has_host = true,
65         },
66         {
67                 .scheme = pb_url_nfs,
68                 .str = "nfs",
69                 .str_len = sizeof("nfs") - 1,
70                 .has_host = true,
71         },
72         {
73                 .scheme = pb_url_sftp,
74                 .str = "sftp",
75                 .str_len = sizeof("sftp") - 1,
76                 .has_host = true,
77         },
78         {
79                 .scheme = pb_url_tftp,
80                 .str = "tftp",
81                 .str_len = sizeof("tftp") - 1,
82                 .has_host = true,
83         },
84 };
85
86 static const struct pb_scheme_info *file_scheme = &schemes[0];
87
88 /**
89  * pb_url_find_scheme - Find the pb_scheme_info for a URL string.
90  */
91
92 static const struct pb_scheme_info *pb_url_scheme_info(
93                 enum pb_url_scheme scheme)
94 {
95         unsigned int i;
96
97         for (i = 0; i < sizeof(schemes) / sizeof(schemes[0]); i++) {
98                 const struct pb_scheme_info *info = &schemes[i];
99
100                 if (info->scheme == scheme)
101                         return info;
102
103         }
104         return NULL;
105 }
106
107 static const struct pb_scheme_info *pb_url_find_scheme(const char *url)
108 {
109         static const int sep_len = sizeof("://") - 1;
110         static const char *sep = "://";
111         unsigned int i, url_len;
112
113         url_len = strlen(url);
114
115         for (i = 0; i < sizeof(schemes) / sizeof(schemes[0]); i++) {
116                 const struct pb_scheme_info *scheme = &schemes[i];
117
118                 if (url_len < scheme->str_len + sep_len)
119                         continue;
120
121                 if (strncmp(url + scheme->str_len, sep, sep_len))
122                         continue;
123
124                 if (strncasecmp(url, scheme->str, scheme->str_len))
125                         continue;
126
127                 return scheme;
128         }
129
130         return NULL;
131 }
132
133 static void pb_url_parse_path(struct pb_url *url)
134 {
135         const char *p = strrchr(url->path, '/');
136
137         talloc_free(url->dir);
138         talloc_free(url->file);
139
140         if (p) {
141                 p++;
142                 url->dir = talloc_strndup(url, url->path, p - url->path);
143                 url->file = talloc_strdup(url, p);
144         } else {
145                 url->dir = NULL;
146                 url->file = talloc_strdup(url, url->path);
147         }
148 }
149
150 /**
151  * pb_url_parse - Parse a remote file URL.
152  * @ctx: The talloc context to associate with the returned string.
153  *
154  * Returns a talloc'ed struct pb_url instance on success, or NULL on error.
155  */
156
157 struct pb_url *pb_url_parse(void *ctx, const char *url_str)
158 {
159         const struct pb_scheme_info *si;
160         struct pb_url *url;
161         const char *p;
162
163         if (!url_str || !*url_str) {
164                 assert(0 && "bad url");
165                 return NULL;
166         }
167
168         url = talloc_zero(ctx, struct pb_url);
169
170         if (!url)
171                 return NULL;
172
173         si = pb_url_find_scheme(url_str);
174         if (si) {
175                 url->scheme = si->scheme;
176                 p = url_str + si->str_len + strlen("://");
177         } else {
178                 url->scheme = file_scheme->scheme;
179                 p = url_str;
180         }
181
182         url->full = talloc_strdup(url, url_str);
183
184         if (url->scheme == pb_url_file) {
185                 url->port = NULL;
186                 url->host = NULL;
187                 url->path = talloc_strdup(url, p);
188         } else {
189                 int len;
190                 const char *col;
191                 const char *path;
192
193                 path = strchr(p, '/');
194
195                 if (!path) {
196                         pb_log("%s: parse path failed '%s'\n", __func__ , p);
197                         goto fail;
198                 }
199
200                 col = strchr(p, ':');
201
202                 if (col) {
203                         len = path - col - 1;
204                         url->port = len ? talloc_strndup(url, col + 1, len)
205                                 : NULL;
206                         len = col - p;
207                         url->host = len ? talloc_strndup(url, p, len) : NULL;
208                 } else {
209                         url->port = NULL;
210                         url->host = talloc_strndup(url, p, path - p);
211                 }
212
213                 /* remove multiple leading slashes */
214                 for (; *path && *(path+1) == '/'; path++)
215                         ;
216
217                 url->path = talloc_strdup(url, path);
218         }
219
220         pb_url_parse_path(url);
221
222         return url;
223
224 fail:
225         talloc_free(url);
226         return NULL;
227 }
228
229 bool is_url(const char *str)
230 {
231         return strstr(str, "://") != NULL;
232 }
233
234 char *pb_url_to_string(struct pb_url *url)
235 {
236         const struct pb_scheme_info *scheme = pb_url_scheme_info(url->scheme);
237         assert(scheme);
238
239         return talloc_asprintf(url, "%s://%s%s", scheme->str,
240                         scheme->has_host ? url->host : "", url->path);
241 }
242
243 static void pb_url_update_full(struct pb_url *url)
244 {
245         talloc_free(url->full);
246         url->full = pb_url_to_string(url);
247 }
248
249 static struct pb_url *pb_url_copy(void *ctx, const struct pb_url *url)
250 {
251         struct pb_url *new_url;
252
253         new_url = talloc(ctx, struct pb_url);
254         new_url->scheme = url->scheme;
255         new_url->full = talloc_strdup(new_url, url->full);
256
257         new_url->host = url->host ? talloc_strdup(new_url, url->host) : NULL;
258         new_url->port = url->port ? talloc_strdup(new_url, url->port) : NULL;
259         new_url->path = url->path ? talloc_strdup(new_url, url->path) : NULL;
260         new_url->dir  = url->dir  ? talloc_strdup(new_url, url->dir)  : NULL;
261         new_url->file = url->file ? talloc_strdup(new_url, url->file) : NULL;
262
263         return new_url;
264 }
265
266 struct pb_url *pb_url_join(void *ctx, const struct pb_url *url, const char *s)
267 {
268         struct pb_url *new_url;
269
270         /* complete url: just parse all info from s */
271         if (is_url(s))
272                 return pb_url_parse(ctx, s);
273
274         new_url = pb_url_copy(ctx, url);
275
276         if (s[0] == '/') {
277                 /* absolute path: replace path of new_url */
278                 talloc_free(new_url->path);
279                 new_url->path = talloc_strdup(new_url, s);
280
281         } else {
282                 /* relative path: join s to existing path. We know that
283                  * url->dir ends with a slash. */
284                 char *tmp = new_url->path;
285                 new_url->path = talloc_asprintf(new_url, "%s%s", url->dir, s);
286                 talloc_free(tmp);
287         }
288
289         /* replace ->dir and ->file with components from ->path */
290         pb_url_parse_path(new_url);
291
292         /* and re-generate the full URL */
293         pb_url_update_full(new_url);
294
295         return new_url;
296 }
297
298 const char *pb_url_scheme_name(enum pb_url_scheme scheme)
299 {
300         const struct pb_scheme_info *info = pb_url_scheme_info(scheme);
301         return info ? info->str : NULL;
302 }