[e1000] Add 82576 support
[gpxe.git] / src / core / uri.c
blobd31aabaf40469bd8752d5c7230210d39f1f9ce37
1 /*
2 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
19 FILE_LICENCE ( GPL2_OR_LATER );
21 /** @file
23 * Uniform Resource Identifiers
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <libgen.h>
31 #include <ctype.h>
32 #include <gpxe/vsprintf.h>
33 #include <gpxe/uri.h>
35 /**
36 * Dump URI for debugging
38 * @v uri URI
40 static void dump_uri ( struct uri *uri ) {
41 if ( ! uri )
42 return;
43 if ( uri->scheme )
44 DBG ( " scheme \"%s\"", uri->scheme );
45 if ( uri->opaque )
46 DBG ( " opaque \"%s\"", uri->opaque );
47 if ( uri->user )
48 DBG ( " user \"%s\"", uri->user );
49 if ( uri->password )
50 DBG ( " password \"%s\"", uri->password );
51 if ( uri->host )
52 DBG ( " host \"%s\"", uri->host );
53 if ( uri->port )
54 DBG ( " port \"%s\"", uri->port );
55 if ( uri->path )
56 DBG ( " path \"%s\"", uri->path );
57 if ( uri->query )
58 DBG ( " query \"%s\"", uri->query );
59 if ( uri->fragment )
60 DBG ( " fragment \"%s\"", uri->fragment );
63 /**
64 * Parse URI
66 * @v uri_string URI as a string
67 * @ret uri URI
69 * Splits a URI into its component parts. The return URI structure is
70 * dynamically allocated and must eventually be freed by calling
71 * uri_put().
73 struct uri * parse_uri ( const char *uri_string ) {
74 struct uri *uri;
75 char *raw;
76 char *tmp;
77 char *path = NULL;
78 char *authority = NULL;
79 size_t raw_len;
81 /* Allocate space for URI struct and a copy of the string */
82 raw_len = ( strlen ( uri_string ) + 1 /* NUL */ );
83 uri = zalloc ( sizeof ( *uri ) + raw_len );
84 if ( ! uri )
85 return NULL;
86 raw = ( ( ( char * ) uri ) + sizeof ( *uri ) );
88 /* Zero URI struct and copy in the raw string */
89 memcpy ( raw, uri_string, raw_len );
91 /* Start by chopping off the fragment, if it exists */
92 if ( ( tmp = strchr ( raw, '#' ) ) ) {
93 *(tmp++) = '\0';
94 uri->fragment = tmp;
97 /* Identify absolute/relative URI. We ignore schemes that are
98 * apparently only a single character long, since otherwise we
99 * misinterpret a DOS-style path name ("C:\path\to\file") as a
100 * URI with scheme="C",opaque="\path\to\file".
102 if ( ( tmp = strchr ( raw, ':' ) ) && ( tmp > ( raw + 1 ) ) ) {
103 /* Absolute URI: identify hierarchical/opaque */
104 uri->scheme = raw;
105 *(tmp++) = '\0';
106 if ( *tmp == '/' ) {
107 /* Absolute URI with hierarchical part */
108 path = tmp;
109 } else {
110 /* Absolute URI with opaque part */
111 uri->opaque = tmp;
113 } else {
114 /* Relative URI */
115 path = raw;
118 /* If we don't have a path (i.e. we have an absolute URI with
119 * an opaque portion, we're already finished processing
121 if ( ! path )
122 goto done;
124 /* Chop off the query, if it exists */
125 if ( ( tmp = strchr ( path, '?' ) ) ) {
126 *(tmp++) = '\0';
127 uri->query = tmp;
130 /* Identify net/absolute/relative path */
131 if ( strncmp ( path, "//", 2 ) == 0 ) {
132 /* Net path. If this is terminated by the first '/'
133 * of an absolute path, then we have no space for a
134 * terminator after the authority field, so shuffle
135 * the authority down by one byte, overwriting one of
136 * the two slashes.
138 authority = ( path + 2 );
139 if ( ( tmp = strchr ( authority, '/' ) ) ) {
140 /* Shuffle down */
141 uri->path = tmp;
142 memmove ( ( authority - 1 ), authority,
143 ( tmp - authority ) );
144 authority--;
145 *(--tmp) = '\0';
147 } else {
148 /* Absolute/relative path */
149 uri->path = path;
152 /* Split authority into user[:password] and host[:port] portions */
153 if ( ( tmp = strchr ( authority, '@' ) ) ) {
154 /* Has user[:password] */
155 *(tmp++) = '\0';
156 uri->host = tmp;
157 uri->user = authority;
158 if ( ( tmp = strchr ( authority, ':' ) ) ) {
159 /* Has password */
160 *(tmp++) = '\0';
161 uri->password = tmp;
163 } else {
164 /* No user:password */
165 uri->host = authority;
168 /* Split host into host[:port] */
169 if ( ( tmp = strchr ( uri->host, ':' ) ) ) {
170 *(tmp++) = '\0';
171 uri->port = tmp;
174 done:
175 DBG ( "URI \"%s\" split into", uri_string );
176 dump_uri ( uri );
177 DBG ( "\n" );
179 return uri;
183 * Get port from URI
185 * @v uri URI, or NULL
186 * @v default_port Default port to use if none specified in URI
187 * @ret port Port
189 unsigned int uri_port ( struct uri *uri, unsigned int default_port ) {
190 if ( ( ! uri ) || ( ! uri->port ) )
191 return default_port;
192 return ( strtoul ( uri->port, NULL, 0 ) );
196 * Unparse URI
198 * @v buf Buffer to fill with URI string
199 * @v size Size of buffer
200 * @v uri URI to write into buffer, or NULL
201 * @ret len Length of URI string
203 int unparse_uri ( char *buf, size_t size, struct uri *uri ) {
204 int used = 0;
206 DBG ( "URI unparsing" );
207 dump_uri ( uri );
208 DBG ( "\n" );
210 /* Special-case NULL URI */
211 if ( ! uri ) {
212 if ( size )
213 buf[0] = '\0';
214 return 0;
217 /* Special-case opaque URIs */
218 if ( uri->opaque ) {
219 return ssnprintf ( ( buf + used ), ( size - used ),
220 "%s:%s", uri->scheme, uri->opaque );
223 /* scheme:// */
224 if ( uri->scheme ) {
225 used += ssnprintf ( ( buf + used ), ( size - used ),
226 "%s://", uri->scheme );
229 /* [user[:password]@]host[:port] */
230 if ( uri->host ) {
231 if ( uri->user ) {
232 used += ssnprintf ( ( buf + used ), ( size - used ),
233 "%s", uri->user );
234 if ( uri->password ) {
235 used += ssnprintf ( ( buf + used ),
236 ( size - used ),
237 ":%s", uri->password );
239 used += ssnprintf ( ( buf + used ), ( size - used ),
240 "@" );
242 used += ssnprintf ( ( buf + used ), ( size - used ), "%s",
243 uri->host );
244 if ( uri->port ) {
245 used += ssnprintf ( ( buf + used ), ( size - used ),
246 ":%s", uri->port );
250 /* /path */
251 if ( uri->path ) {
252 used += ssnprintf ( ( buf + used ), ( size - used ),
253 "%s", uri->path );
256 /* ?query */
257 if ( uri->query ) {
258 used += ssnprintf ( ( buf + used ), ( size - used ),
259 "?%s", uri->query );
262 /* #fragment */
263 if ( uri->fragment ) {
264 used += ssnprintf ( ( buf + used ), ( size - used ),
265 "#%s", uri->fragment );
268 return used;
272 * Duplicate URI
274 * @v uri URI
275 * @ret uri Duplicate URI
277 * Creates a modifiable copy of a URI.
279 struct uri * uri_dup ( struct uri *uri ) {
280 size_t len = ( unparse_uri ( NULL, 0, uri ) + 1 );
281 char buf[len];
283 unparse_uri ( buf, len, uri );
284 return parse_uri ( buf );
288 * Resolve base+relative path
290 * @v base_uri Base path
291 * @v relative_uri Relative path
292 * @ret resolved_uri Resolved path
294 * Takes a base path (e.g. "/var/lib/tftpboot/vmlinuz" and a relative
295 * path (e.g. "initrd.gz") and produces a new path
296 * (e.g. "/var/lib/tftpboot/initrd.gz"). Note that any non-directory
297 * portion of the base path will automatically be stripped; this
298 * matches the semantics used when resolving the path component of
299 * URIs.
301 char * resolve_path ( const char *base_path,
302 const char *relative_path ) {
303 size_t base_len = ( strlen ( base_path ) + 1 );
304 char base_path_copy[base_len];
305 char *base_tmp = base_path_copy;
306 char *resolved;
308 /* If relative path is absolute, just re-use it */
309 if ( relative_path[0] == '/' )
310 return strdup ( relative_path );
312 /* Create modifiable copy of path for dirname() */
313 memcpy ( base_tmp, base_path, base_len );
314 base_tmp = dirname ( base_tmp );
316 /* Process "./" and "../" elements */
317 while ( *relative_path == '.' ) {
318 relative_path++;
319 if ( *relative_path == 0 ) {
320 /* Do nothing */
321 } else if ( *relative_path == '/' ) {
322 relative_path++;
323 } else if ( *relative_path == '.' ) {
324 relative_path++;
325 if ( *relative_path == 0 ) {
326 base_tmp = dirname ( base_tmp );
327 } else if ( *relative_path == '/' ) {
328 base_tmp = dirname ( base_tmp );
329 relative_path++;
330 } else {
331 relative_path -= 2;
332 break;
334 } else {
335 relative_path--;
336 break;
340 /* Create and return new path */
341 if ( asprintf ( &resolved, "%s%s%s", base_tmp,
342 ( ( base_tmp[ strlen ( base_tmp ) - 1 ] == '/' ) ?
343 "" : "/" ), relative_path ) < 0 )
344 return NULL;
346 return resolved;
350 * Resolve base+relative URI
352 * @v base_uri Base URI, or NULL
353 * @v relative_uri Relative URI
354 * @ret resolved_uri Resolved URI
356 * Takes a base URI (e.g. "http://etherboot.org/kernels/vmlinuz" and a
357 * relative URI (e.g. "../initrds/initrd.gz") and produces a new URI
358 * (e.g. "http://etherboot.org/initrds/initrd.gz").
360 struct uri * resolve_uri ( struct uri *base_uri,
361 struct uri *relative_uri ) {
362 struct uri tmp_uri;
363 char *tmp_path = NULL;
364 struct uri *new_uri;
366 /* If relative URI is absolute, just re-use it */
367 if ( uri_is_absolute ( relative_uri ) || ( ! base_uri ) )
368 return uri_get ( relative_uri );
370 /* Mangle URI */
371 memcpy ( &tmp_uri, base_uri, sizeof ( tmp_uri ) );
372 if ( relative_uri->path ) {
373 tmp_path = resolve_path ( ( base_uri->path ?
374 base_uri->path : "/" ),
375 relative_uri->path );
376 tmp_uri.path = tmp_path;
377 tmp_uri.query = relative_uri->query;
378 tmp_uri.fragment = relative_uri->fragment;
379 } else if ( relative_uri->query ) {
380 tmp_uri.query = relative_uri->query;
381 tmp_uri.fragment = relative_uri->fragment;
382 } else if ( relative_uri->fragment ) {
383 tmp_uri.fragment = relative_uri->fragment;
386 /* Create demangled URI */
387 new_uri = uri_dup ( &tmp_uri );
388 free ( tmp_path );
389 return new_uri;
393 * Test for unreserved URI characters
395 * @v c Character to test
396 * @ret is_unreserved Character is an unreserved character
398 static int is_unreserved_uri_char ( int c ) {
399 /* According to RFC3986, the unreserved character set is
401 * A-Z a-z 0-9 - _ . ~
403 return ( isupper ( c ) || islower ( c ) || isdigit ( c ) ||
404 ( c == '-' ) || ( c == '_' ) ||
405 ( c == '.' ) || ( c == '~' ) );
409 * URI-encode string
411 * @v raw_string String to be URI-encoded
412 * @v buf Buffer to contain encoded string
413 * @v len Length of buffer
414 * @ret len Length of encoded string (excluding NUL)
416 size_t uri_encode ( const char *raw_string, char *buf, size_t len ) {
417 ssize_t remaining = len;
418 size_t used;
419 unsigned char c;
421 if ( len )
422 buf[0] = '\0';
424 while ( ( c = *(raw_string++) ) ) {
425 if ( is_unreserved_uri_char ( c ) ) {
426 used = ssnprintf ( buf, remaining, "%c", c );
427 } else {
428 used = ssnprintf ( buf, remaining, "%%%02X", c );
430 buf += used;
431 remaining -= used;
434 return ( len - remaining );
438 * Decode URI-encoded string
440 * @v encoded_string URI-encoded string
441 * @v buf Buffer to contain decoded string
442 * @v len Length of buffer
443 * @ret len Length of decoded string (excluding NUL)
445 size_t uri_decode ( const char *encoded_string, char *buf, size_t len ) {
446 ssize_t remaining = len;
447 char hexbuf[3];
448 char *hexbuf_end;
449 unsigned char c;
451 if ( len )
452 buf[0] = '\0';
454 while ( *encoded_string ) {
455 if ( *encoded_string == '%' ) {
456 encoded_string++;
457 snprintf ( hexbuf, sizeof ( hexbuf ), "%s",
458 encoded_string );
459 c = strtoul ( hexbuf, &hexbuf_end, 16 );
460 encoded_string += ( hexbuf_end - hexbuf );
461 } else {
462 c = *(encoded_string++);
464 ssnprintf ( buf++, remaining--, "%c", c );
466 return ( len - remaining );