/** * Copy URI fields * * @v src Source URI * @v dest Destination URI, or NULL to calculate length * @ret len Length of raw URI */ static size_t uri_copy_fields ( const struct uri *src, struct uri *dest ) { size_t len = sizeof ( *dest ); char *out = ( ( void * ) dest + len ); unsigned int field; size_t field_len; /* Copy existent fields */ for ( field = 0 ; field < URI_FIELDS ; field++ ) { /* Skip non-existent fields */ if ( ! uri_field ( src, field ) ) continue; /* Calculate field length */ field_len = ( strlen ( uri_field ( src, field ) ) + 1 /* NUL */ ); len += field_len; /* Copy field, if applicable */ if ( dest ) { memcpy ( out, uri_field ( src, field ), field_len ); uri_field ( dest, field ) = out; out += field_len; } } return len; }
/** * Format URI * * @v uri URI * @v buf Buffer to fill with URI string * @v size Size of buffer * @ret len Length of URI string */ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { static const char prefixes[URI_FIELDS] = { [URI_OPAQUE] = ':', [URI_PASSWORD] = ':', [URI_PORT] = ':', [URI_PATH] = '/', [URI_QUERY] = '?', [URI_FRAGMENT] = '#', }; char prefix; size_t used = 0; unsigned int field; /* Ensure buffer is NUL-terminated */ if ( len ) buf[0] = '\0'; /* Special-case NULL URI */ if ( ! uri ) return 0; /* Generate fields */ for ( field = 0 ; field < URI_FIELDS ; field++ ) { /* Skip non-existent fields */ if ( ! uri_field ( uri, field ) ) continue; /* Prefix this field, if applicable */ prefix = prefixes[field]; if ( ( field == URI_HOST ) && ( uri->user != NULL ) ) prefix = '@'; if ( ( field == URI_PATH ) && ( uri->path[0] == '/' ) ) prefix = '\0'; if ( prefix ) { used += ssnprintf ( ( buf + used ), ( len - used ), "%c", prefix ); } /* Encode this field */ used += uri_encode ( uri_field ( uri, field ), field, ( buf + used ), ( len - used ) ); /* Suffix this field, if applicable */ if ( ( field == URI_SCHEME ) && ( ! uri->opaque ) ) { used += ssnprintf ( ( buf + used ), ( len - used ), "://" ); } } if ( len ) { DBGC ( uri, "URI formatted" ); uri_dump ( uri ); DBGC ( uri, " to \"%s%s\"\n", buf, ( ( used > len ) ? "<TRUNCATED>" : "" ) ); } return used; }
/** * Decode URI field in-place * * @v uri URI * @v field URI field index */ static void uri_decode_inplace ( struct uri *uri, unsigned int field ) { const char *encoded = uri_field ( uri, field ); char *decoded = ( ( char * ) encoded ); size_t len; /* Do nothing if field is not present */ if ( ! encoded ) return; /* Decode field in place */ len = uri_decode ( encoded, decoded, strlen ( encoded ) ); /* Terminate decoded string */ decoded[len] = '\0'; }
/** * Parse URI * * @v uri_string URI as a string * @ret uri URI * * Splits a URI into its component parts. The return URI structure is * dynamically allocated and must eventually be freed by calling * uri_put(). */ struct uri * parse_uri ( const char *uri_string ) { struct uri *uri; struct parameters *params; char *raw; char *tmp; char *path; char *authority; size_t raw_len; unsigned int field; /* Allocate space for URI struct and a copy of the string */ raw_len = ( strlen ( uri_string ) + 1 /* NUL */ ); uri = zalloc ( sizeof ( *uri ) + raw_len ); if ( ! uri ) return NULL; ref_init ( &uri->refcnt, uri_free ); raw = ( ( ( void * ) uri ) + sizeof ( *uri ) ); /* Copy in the raw string */ memcpy ( raw, uri_string, raw_len ); /* Identify the parameter list, if present */ if ( ( tmp = strstr ( raw, "##params" ) ) ) { *tmp = '\0'; tmp += 8 /* "##params" */; params = find_parameters ( *tmp ? ( tmp + 1 ) : NULL ); if ( params ) { uri->params = claim_parameters ( params ); } else { /* Ignore non-existent submission blocks */ } } /* Chop off the fragment, if it exists */ if ( ( tmp = strchr ( raw, '#' ) ) ) { *(tmp++) = '\0'; uri->fragment = tmp; } /* Identify absolute/relative URI */ if ( ( tmp = strchr ( raw, ':' ) ) ) { /* Absolute URI: identify hierarchical/opaque */ uri->scheme = raw; *(tmp++) = '\0'; if ( *tmp == '/' ) { /* Absolute URI with hierarchical part */ path = tmp; } else { /* Absolute URI with opaque part */ uri->opaque = tmp; path = NULL; } } else { /* Relative URI */ path = raw; } /* If we don't have a path (i.e. we have an absolute URI with * an opaque portion, we're already finished processing */ if ( ! path ) goto done; /* Chop off the query, if it exists */ if ( ( tmp = strchr ( path, '?' ) ) ) { *(tmp++) = '\0'; uri->query = tmp; } /* If we have no path remaining, then we're already finished * processing. */ if ( ! path[0] ) goto done; /* Identify net/absolute/relative path */ if ( strncmp ( path, "//", 2 ) == 0 ) { /* Net path. If this is terminated by the first '/' * of an absolute path, then we have no space for a * terminator after the authority field, so shuffle * the authority down by one byte, overwriting one of * the two slashes. */ authority = ( path + 2 ); if ( ( tmp = strchr ( authority, '/' ) ) ) { /* Shuffle down */ uri->path = tmp; memmove ( ( authority - 1 ), authority, ( tmp - authority ) ); authority--; *(--tmp) = '\0'; } } else { /* Absolute/relative path */ uri->path = path; authority = NULL; } /* If we don't have an authority (i.e. we have a non-net * path), we're already finished processing */ if ( ! authority ) goto done; /* Split authority into user[:password] and host[:port] portions */ if ( ( tmp = strchr ( authority, '@' ) ) ) { /* Has user[:password] */ *(tmp++) = '\0'; uri->host = tmp; uri->user = authority; if ( ( tmp = strchr ( authority, ':' ) ) ) { /* Has password */ *(tmp++) = '\0'; uri->password = tmp; } } else { /* No user:password */ uri->host = authority; } /* Split host into host[:port] */ if ( ( uri->host[ strlen ( uri->host ) - 1 ] != ']' ) && ( tmp = strrchr ( uri->host, ':' ) ) ) { *(tmp++) = '\0'; uri->port = tmp; } /* Decode fields in-place */ for ( field = 0 ; field < URI_FIELDS ; field++ ) { if ( uri_field ( uri, field ) ) uri_decode ( ( char * ) uri_field ( uri, field ) ); } done: DBGC ( uri, "URI parsed \"%s\" to", uri_string ); uri_dump ( uri ); DBGC ( uri, "\n" ); return uri; }