|
@@ -76,6 +76,7 @@ struct uri * parse_uri ( const char *uri_string ) {
|
76
|
76
|
char *tmp;
|
77
|
77
|
char *path = NULL;
|
78
|
78
|
char *authority = NULL;
|
|
79
|
+ int i;
|
79
|
80
|
size_t raw_len;
|
80
|
81
|
|
81
|
82
|
/* Allocate space for URI struct and a copy of the string */
|
|
@@ -171,6 +172,14 @@ struct uri * parse_uri ( const char *uri_string ) {
|
171
|
172
|
uri->port = tmp;
|
172
|
173
|
}
|
173
|
174
|
|
|
175
|
+ /* Decode fields that should be decoded */
|
|
176
|
+ for ( i = URI_FIRST_FIELD; i <= URI_LAST_FIELD; i++ ) {
|
|
177
|
+ const char *field = uri_get_field ( uri, i );
|
|
178
|
+ if ( field && ( URI_ENCODED & ( 1 << i ) ) )
|
|
179
|
+ uri_decode ( field, ( char * ) field,
|
|
180
|
+ strlen ( field ) + 1 /* NUL */ );
|
|
181
|
+ }
|
|
182
|
+
|
174
|
183
|
done:
|
175
|
184
|
DBG ( "URI \"%s\" split into", uri_string );
|
176
|
185
|
dump_uri ( uri );
|
|
@@ -198,10 +207,19 @@ unsigned int uri_port ( struct uri *uri, unsigned int default_port ) {
|
198
|
207
|
* @v buf Buffer to fill with URI string
|
199
|
208
|
* @v size Size of buffer
|
200
|
209
|
* @v uri URI to write into buffer, or NULL
|
|
210
|
+ * @v fields Bitmask of fields to include in URI string, or URI_ALL
|
201
|
211
|
* @ret len Length of URI string
|
202
|
212
|
*/
|
203
|
|
-int unparse_uri ( char *buf, size_t size, struct uri *uri ) {
|
|
213
|
+int unparse_uri ( char *buf, size_t size, struct uri *uri,
|
|
214
|
+ unsigned int fields ) {
|
|
215
|
+ /* List of characters that typically go before certain fields */
|
|
216
|
+ static char separators[] = { /* scheme */ 0, /* opaque */ ':',
|
|
217
|
+ /* user */ 0, /* password */ ':',
|
|
218
|
+ /* host */ '@', /* port */ ':',
|
|
219
|
+ /* path */ 0, /* query */ '?',
|
|
220
|
+ /* fragment */ '#' };
|
204
|
221
|
int used = 0;
|
|
222
|
+ int i;
|
205
|
223
|
|
206
|
224
|
DBG ( "URI unparsing" );
|
207
|
225
|
dump_uri ( uri );
|
|
@@ -214,55 +232,39 @@ int unparse_uri ( char *buf, size_t size, struct uri *uri ) {
|
214
|
232
|
return 0;
|
215
|
233
|
}
|
216
|
234
|
|
217
|
|
- /* Special-case opaque URIs */
|
218
|
|
- if ( uri->opaque ) {
|
219
|
|
- return ssnprintf ( ( buf + used ), ( size - used ),
|
220
|
|
- "%s:%s", uri->scheme, uri->opaque );
|
221
|
|
- }
|
222
|
|
-
|
223
|
|
- /* scheme:// */
|
224
|
|
- if ( uri->scheme ) {
|
225
|
|
- used += ssnprintf ( ( buf + used ), ( size - used ),
|
226
|
|
- "%s://", uri->scheme );
|
227
|
|
- }
|
228
|
|
-
|
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 );
|
|
235
|
+ /* Iterate through requested fields */
|
|
236
|
+ for ( i = URI_FIRST_FIELD; i <= URI_LAST_FIELD; i++ ) {
|
|
237
|
+ const char *field = uri_get_field ( uri, i );
|
|
238
|
+ char sep = separators[i];
|
|
239
|
+
|
|
240
|
+ /* Ensure `fields' only contains bits for fields that exist */
|
|
241
|
+ if ( ! field )
|
|
242
|
+ fields &= ~( 1 << i );
|
|
243
|
+
|
|
244
|
+ /* Store this field if we were asked to */
|
|
245
|
+ if ( fields & ( 1 << i ) ) {
|
|
246
|
+ /* Print :// if we're non-opaque and had a scheme */
|
|
247
|
+ if ( ( fields & URI_SCHEME_BIT ) &&
|
|
248
|
+ ( i > URI_OPAQUE ) ) {
|
|
249
|
+ used += ssnprintf ( buf + used, size - used,
|
|
250
|
+ "://" );
|
|
251
|
+ /* Only print :// once */
|
|
252
|
+ fields &= ~URI_SCHEME_BIT;
|
238
|
253
|
}
|
239
|
|
- used += ssnprintf ( ( buf + used ), ( size - used ),
|
240
|
|
- "@" );
|
241
|
|
- }
|
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 );
|
247
|
|
- }
|
248
|
|
- }
|
249
|
|
-
|
250
|
|
- /* /path */
|
251
|
|
- if ( uri->path ) {
|
252
|
|
- used += ssnprintf ( ( buf + used ), ( size - used ),
|
253
|
|
- "%s", uri->path );
|
254
|
|
- }
|
255
|
254
|
|
256
|
|
- /* ?query */
|
257
|
|
- if ( uri->query ) {
|
258
|
|
- used += ssnprintf ( ( buf + used ), ( size - used ),
|
259
|
|
- "?%s", uri->query );
|
260
|
|
- }
|
261
|
|
-
|
262
|
|
- /* #fragment */
|
263
|
|
- if ( uri->fragment ) {
|
264
|
|
- used += ssnprintf ( ( buf + used ), ( size - used ),
|
265
|
|
- "#%s", uri->fragment );
|
|
255
|
+ /* Only print separator if an earlier field exists */
|
|
256
|
+ if ( sep && ( fields & ( ( 1 << i ) - 1 ) ) )
|
|
257
|
+ used += ssnprintf ( buf + used, size - used,
|
|
258
|
+ "%c", sep );
|
|
259
|
+
|
|
260
|
+ /* Print contents of field, possibly encoded */
|
|
261
|
+ if ( URI_ENCODED & ( 1 << i ) )
|
|
262
|
+ used += uri_encode ( field, buf + used,
|
|
263
|
+ size - used, i );
|
|
264
|
+ else
|
|
265
|
+ used += ssnprintf ( buf + used, size - used,
|
|
266
|
+ "%s", field );
|
|
267
|
+ }
|
266
|
268
|
}
|
267
|
269
|
|
268
|
270
|
return used;
|
|
@@ -277,10 +279,10 @@ int unparse_uri ( char *buf, size_t size, struct uri *uri ) {
|
277
|
279
|
* Creates a modifiable copy of a URI.
|
278
|
280
|
*/
|
279
|
281
|
struct uri * uri_dup ( struct uri *uri ) {
|
280
|
|
- size_t len = ( unparse_uri ( NULL, 0, uri ) + 1 );
|
|
282
|
+ size_t len = ( unparse_uri ( NULL, 0, uri, URI_ALL ) + 1 );
|
281
|
283
|
char buf[len];
|
282
|
284
|
|
283
|
|
- unparse_uri ( buf, len, uri );
|
|
285
|
+ unparse_uri ( buf, len, uri, URI_ALL );
|
284
|
286
|
return parse_uri ( buf );
|
285
|
287
|
}
|
286
|
288
|
|
|
@@ -393,16 +395,31 @@ struct uri * resolve_uri ( struct uri *base_uri,
|
393
|
395
|
* Test for unreserved URI characters
|
394
|
396
|
*
|
395
|
397
|
* @v c Character to test
|
|
398
|
+ * @v field Field of URI in which character lies
|
396
|
399
|
* @ret is_unreserved Character is an unreserved character
|
397
|
400
|
*/
|
398
|
|
-static int is_unreserved_uri_char ( int c ) {
|
|
401
|
+static int is_unreserved_uri_char ( int c, int field ) {
|
399
|
402
|
/* According to RFC3986, the unreserved character set is
|
400
|
403
|
*
|
401
|
404
|
* A-Z a-z 0-9 - _ . ~
|
|
405
|
+ *
|
|
406
|
+ * but we also pass & ; = in queries, / in paths,
|
|
407
|
+ * and everything in opaques
|
402
|
408
|
*/
|
403
|
|
- return ( isupper ( c ) || islower ( c ) || isdigit ( c ) ||
|
404
|
|
- ( c == '-' ) || ( c == '_' ) ||
|
405
|
|
- ( c == '.' ) || ( c == '~' ) );
|
|
409
|
+ int ok = ( isupper ( c ) || islower ( c ) || isdigit ( c ) ||
|
|
410
|
+ ( c == '-' ) || ( c == '_' ) ||
|
|
411
|
+ ( c == '.' ) || ( c == '~' ) );
|
|
412
|
+
|
|
413
|
+ if ( field == URI_QUERY )
|
|
414
|
+ ok = ok || ( c == ';' ) || ( c == '&' ) || ( c == '=' );
|
|
415
|
+
|
|
416
|
+ if ( field == URI_PATH )
|
|
417
|
+ ok = ok || ( c == '/' );
|
|
418
|
+
|
|
419
|
+ if ( field == URI_OPAQUE )
|
|
420
|
+ ok = 1;
|
|
421
|
+
|
|
422
|
+ return ok;
|
406
|
423
|
}
|
407
|
424
|
|
408
|
425
|
/**
|
|
@@ -411,18 +428,20 @@ static int is_unreserved_uri_char ( int c ) {
|
411
|
428
|
* @v raw_string String to be URI-encoded
|
412
|
429
|
* @v buf Buffer to contain encoded string
|
413
|
430
|
* @v len Length of buffer
|
|
431
|
+ * @v field Field of URI in which string lies
|
414
|
432
|
* @ret len Length of encoded string (excluding NUL)
|
415
|
433
|
*/
|
416
|
|
-size_t uri_encode ( const char *raw_string, char *buf, size_t len ) {
|
|
434
|
+size_t uri_encode ( const char *raw_string, char *buf, ssize_t len,
|
|
435
|
+ int field ) {
|
417
|
436
|
ssize_t remaining = len;
|
418
|
437
|
size_t used;
|
419
|
438
|
unsigned char c;
|
420
|
439
|
|
421
|
|
- if ( len )
|
|
440
|
+ if ( len > 0 )
|
422
|
441
|
buf[0] = '\0';
|
423
|
442
|
|
424
|
443
|
while ( ( c = *(raw_string++) ) ) {
|
425
|
|
- if ( is_unreserved_uri_char ( c ) ) {
|
|
444
|
+ if ( is_unreserved_uri_char ( c, field ) ) {
|
426
|
445
|
used = ssnprintf ( buf, remaining, "%c", c );
|
427
|
446
|
} else {
|
428
|
447
|
used = ssnprintf ( buf, remaining, "%%%02X", c );
|
|
@@ -441,17 +460,17 @@ size_t uri_encode ( const char *raw_string, char *buf, size_t len ) {
|
441
|
460
|
* @v buf Buffer to contain decoded string
|
442
|
461
|
* @v len Length of buffer
|
443
|
462
|
* @ret len Length of decoded string (excluding NUL)
|
|
463
|
+ *
|
|
464
|
+ * This function may be used in-place, with @a buf the same as
|
|
465
|
+ * @a encoded_string.
|
444
|
466
|
*/
|
445
|
|
-size_t uri_decode ( const char *encoded_string, char *buf, size_t len ) {
|
446
|
|
- ssize_t remaining = len;
|
|
467
|
+size_t uri_decode ( const char *encoded_string, char *buf, ssize_t len ) {
|
|
468
|
+ ssize_t remaining;
|
447
|
469
|
char hexbuf[3];
|
448
|
470
|
char *hexbuf_end;
|
449
|
471
|
unsigned char c;
|
450
|
472
|
|
451
|
|
- if ( len )
|
452
|
|
- buf[0] = '\0';
|
453
|
|
-
|
454
|
|
- while ( *encoded_string ) {
|
|
473
|
+ for ( remaining = len; *encoded_string; remaining-- ) {
|
455
|
474
|
if ( *encoded_string == '%' ) {
|
456
|
475
|
encoded_string++;
|
457
|
476
|
snprintf ( hexbuf, sizeof ( hexbuf ), "%s",
|
|
@@ -461,7 +480,12 @@ size_t uri_decode ( const char *encoded_string, char *buf, size_t len ) {
|
461
|
480
|
} else {
|
462
|
481
|
c = *(encoded_string++);
|
463
|
482
|
}
|
464
|
|
- ssnprintf ( buf++, remaining--, "%c", c );
|
|
483
|
+ if ( remaining > 1 )
|
|
484
|
+ *buf++ = c;
|
465
|
485
|
}
|
|
486
|
+
|
|
487
|
+ if ( len )
|
|
488
|
+ *buf = 0;
|
|
489
|
+
|
466
|
490
|
return ( len - remaining );
|
467
|
491
|
}
|