diff options
Diffstat (limited to 'src/util/util.c')
-rw-r--r-- | src/util/util.c | 428 |
1 files changed, 326 insertions, 102 deletions
diff --git a/src/util/util.c b/src/util/util.c index 3341aa295..0520d0318 100644 --- a/src/util/util.c +++ b/src/util/util.c | |||
@@ -193,89 +193,57 @@ is_reserved (char c) | |||
193 | return GNUNET_YES; | 193 | return GNUNET_YES; |
194 | } | 194 | } |
195 | 195 | ||
196 | /** | ||
197 | * Get the length of a string after it has been | ||
198 | * urlencoded. | ||
199 | * | ||
200 | * @param s the string | ||
201 | * @returns the size of the urlencoded @a s | ||
202 | */ | ||
203 | static size_t | ||
204 | urlencode_len (const char *s) | ||
205 | { | ||
206 | size_t len = 0; | ||
207 | for (; *s != '\0'; len++, s++) | ||
208 | if (GNUNET_YES == is_reserved (*s)) | ||
209 | len += 2; | ||
210 | return len; | ||
211 | } | ||
196 | 212 | ||
197 | /** | 213 | /** |
198 | * URL-encode a string according to rfc3986. | 214 | * URL-encode a string according to rfc3986. |
199 | * | 215 | * |
216 | * @param buf buffer to write the result to | ||
200 | * @param s string to encode | 217 | * @param s string to encode |
201 | * @returns the urlencoded string, the caller must free it with GNUNET_free | ||
202 | */ | 218 | */ |
203 | char * | 219 | static void |
204 | TALER_urlencode (const char *s) | 220 | buffer_write_urlencode (struct TALER_Buffer *buf, const char *s) |
205 | { | 221 | { |
206 | unsigned int new_size; | 222 | TALER_buffer_ensure_remaining (buf, urlencode_len (s) + 1); |
207 | unsigned int i; | ||
208 | unsigned int t; | ||
209 | char *out; | ||
210 | 223 | ||
211 | new_size = strlen (s); | 224 | for (size_t i = 0; i < strlen (s); i++) |
212 | for (i = 0; i < strlen (s); i++) | ||
213 | if (GNUNET_YES == is_reserved (s[i])) | ||
214 | new_size += 2; | ||
215 | out = GNUNET_malloc (new_size + 1); | ||
216 | for (i = 0, t = 0; i < strlen (s); i++, t++) | ||
217 | { | 225 | { |
218 | if (GNUNET_YES == is_reserved (s[i])) | 226 | if (GNUNET_YES == is_reserved (s[i])) |
219 | { | 227 | TALER_buffer_write_fstr (buf, "%%%02X", s[i]); |
220 | snprintf (&out[t], 4, "%%%02X", s[i]); | 228 | else |
221 | t += 2; | 229 | buf->mem[buf->position++] = s[i]; |
222 | continue; | ||
223 | } | ||
224 | out[t] = s[i]; | ||
225 | } | 230 | } |
226 | return out; | ||
227 | } | 231 | } |
228 | 232 | ||
229 | 233 | ||
230 | /** | 234 | /** |
231 | * Grow a string in a buffer with the given size. | 235 | * URL-encode a string according to rfc3986. |
232 | * The buffer is re-allocated if necessary. | ||
233 | * | ||
234 | * @param s pointer to string buffer | ||
235 | * @param p the string to append | ||
236 | * @param n pointer to the allocated size of n | ||
237 | * @returns pointer to the resulting buffer, | ||
238 | * might differ from @a s (!!) | ||
239 | */ | ||
240 | static char * | ||
241 | grow_string (char **s, const char *p, size_t *n) | ||
242 | { | ||
243 | for (; strlen (*s) + strlen (p) >= *n; *n *= 2); | ||
244 | *s = GNUNET_realloc (*s, *n); | ||
245 | GNUNET_assert (NULL != s); | ||
246 | strncat (*s, p, *n); | ||
247 | return *s; | ||
248 | } | ||
249 | |||
250 | |||
251 | /** | ||
252 | * Grow a string in a buffer with the given size. | ||
253 | * The buffer is re-allocated if necessary. | ||
254 | * | ||
255 | * Ensures that slashes are removed or added when joining paths. | ||
256 | * | 236 | * |
257 | * @param s pointer to string buffer | 237 | * @param s string to encode |
258 | * @param p the string to append | 238 | * @returns the urlencoded string, the caller must free it with GNUNET_free |
259 | * @param n pointer to the allocated size of n | ||
260 | * @returns pointer to the resulting buffer, | ||
261 | * might differ from @a s (!!) | ||
262 | */ | 239 | */ |
263 | static char * | 240 | char * |
264 | grow_string_path (char **s, const char *p, size_t *n) | 241 | TALER_urlencode (const char *s) |
265 | { | 242 | { |
266 | char a = (0 == strlen (*s)) ? '\0' : (*s)[strlen (*s) - 1]; | 243 | struct TALER_Buffer buf = { 0 }; |
267 | char b = (0 == strlen (p)) ? '\0' : p[0]; | ||
268 | 244 | ||
269 | if ( (a == '/') && (b == '/')) | 245 | buffer_write_urlencode (&buf, s); |
270 | { | 246 | return TALER_buffer_reap_str (&buf); |
271 | p++; | ||
272 | } | ||
273 | else if ( (a != '/') && (b != '/')) | ||
274 | { | ||
275 | if (NULL == (*s = grow_string (s, "/", n))) | ||
276 | return NULL; | ||
277 | } | ||
278 | return grow_string (s, p, n); | ||
279 | } | 247 | } |
280 | 248 | ||
281 | 249 | ||
@@ -286,36 +254,73 @@ grow_string_path (char **s, const char *p, size_t *n) | |||
286 | * @param path path of the url | 254 | * @param path path of the url |
287 | * @param ... NULL-terminated key-value pairs (char *) for query parameters, | 255 | * @param ... NULL-terminated key-value pairs (char *) for query parameters, |
288 | * the value will be url-encoded | 256 | * the value will be url-encoded |
289 | * @returns the URL, must be freed with #GNUNET_free | 257 | * @returns the URL (must be freed with #GNUNET_free) or |
258 | * NULL if an error occured. | ||
290 | */ | 259 | */ |
291 | char * | 260 | char * |
292 | TALER_url_join (const char *base_url, | 261 | TALER_url_join (const char *base_url, |
293 | const char *path, | 262 | const char *path, |
294 | ...) | 263 | ...) |
295 | { | 264 | { |
296 | size_t n = 256; | ||
297 | char *res = GNUNET_malloc (n); | ||
298 | unsigned int iparam = 0; | 265 | unsigned int iparam = 0; |
299 | char *enc; | ||
300 | va_list args; | 266 | va_list args; |
267 | struct TALER_Buffer buf = { 0 }; | ||
268 | size_t len = 0; | ||
301 | 269 | ||
302 | GNUNET_assert (NULL != res); | ||
303 | GNUNET_assert (NULL != base_url); | 270 | GNUNET_assert (NULL != base_url); |
304 | GNUNET_assert (NULL != path); | 271 | GNUNET_assert (NULL != path); |
305 | GNUNET_assert (strlen (base_url) > 0); | ||
306 | 272 | ||
307 | // Must be an actual base URL! | 273 | if (strlen (base_url) == 0) |
308 | GNUNET_assert ('/' == base_url[strlen (base_url) - 1]); | 274 | { |
275 | /* base URL can't be empty */ | ||
276 | GNUNET_break (0); | ||
277 | return NULL; | ||
278 | } | ||
309 | 279 | ||
310 | // Path must be relative to existing path of base URL | 280 | if ('/' != base_url[strlen (base_url) - 1]) |
311 | GNUNET_assert ('/' != path[0]); | 281 | { |
282 | /* Must be an actual base URL! */ | ||
283 | GNUNET_break (0); | ||
284 | return NULL; | ||
285 | } | ||
312 | 286 | ||
313 | grow_string (&res, base_url, &n); | 287 | if ('/' == path[0]) |
288 | { | ||
289 | /* The path must be relative. */ | ||
290 | GNUNET_break (0); | ||
291 | return NULL; | ||
292 | } | ||
314 | 293 | ||
315 | grow_string_path (&res, path, &n); | 294 | // Path should be relative to existing path of base URL |
295 | GNUNET_break ('/' != path[0]); | ||
296 | |||
297 | if ('/' == path[0]) | ||
298 | GNUNET_break (0); | ||
299 | |||
300 | /* 1st pass: compute length */ | ||
301 | len += strlen (base_url) + strlen (path); | ||
316 | 302 | ||
317 | va_start (args, path); | 303 | va_start (args, path); |
304 | while (1) | ||
305 | { | ||
306 | char *key; | ||
307 | char *value; | ||
308 | key = va_arg (args, char *); | ||
309 | if (NULL == key) | ||
310 | break; | ||
311 | value = va_arg (args, char *); | ||
312 | if (NULL == value) | ||
313 | continue; | ||
314 | len += urlencode_len (value) + strlen (key) + 2; | ||
315 | } | ||
316 | va_end (args); | ||
318 | 317 | ||
318 | TALER_buffer_prealloc (&buf, len); | ||
319 | |||
320 | TALER_buffer_write_str (&buf, base_url); | ||
321 | TALER_buffer_write_str (&buf, path); | ||
322 | |||
323 | va_start (args, path); | ||
319 | while (1) | 324 | while (1) |
320 | { | 325 | { |
321 | char *key; | 326 | char *key; |
@@ -326,18 +331,15 @@ TALER_url_join (const char *base_url, | |||
326 | value = va_arg (args, char *); | 331 | value = va_arg (args, char *); |
327 | if (NULL == value) | 332 | if (NULL == value) |
328 | continue; | 333 | continue; |
329 | grow_string (&res, (0 == iparam) ? "?" : "&", &n); | 334 | TALER_buffer_write_str (&buf, (0 == iparam) ? "?" : "&"); |
330 | iparam++; | 335 | iparam++; |
331 | grow_string (&res, key, &n); | 336 | TALER_buffer_write_str (&buf, key); |
332 | grow_string (&res, "=", &n); | 337 | TALER_buffer_write_str (&buf, "="); |
333 | enc = TALER_urlencode (value); | 338 | buffer_write_urlencode (&buf, value); |
334 | grow_string (&res, enc, &n); | ||
335 | GNUNET_free (enc); | ||
336 | } | 339 | } |
337 | |||
338 | va_end (args); | 340 | va_end (args); |
339 | 341 | ||
340 | return res; | 342 | return TALER_buffer_reap_str (&buf); |
341 | } | 343 | } |
342 | 344 | ||
343 | 345 | ||
@@ -353,25 +355,45 @@ TALER_url_join (const char *base_url, | |||
353 | * @returns the URL, must be freed with #GNUNET_free | 355 | * @returns the URL, must be freed with #GNUNET_free |
354 | */ | 356 | */ |
355 | char * | 357 | char * |
356 | url_absolute_raw_va (const char *proto, | 358 | TALER_url_absolute_raw_va (const char *proto, |
357 | const char *host, | 359 | const char *host, |
358 | const char *prefix, | 360 | const char *prefix, |
359 | const char *path, | 361 | const char *path, |
360 | va_list args) | 362 | va_list args) |
361 | { | 363 | { |
362 | size_t n = 256; | 364 | struct TALER_Buffer buf = { 0 }; |
363 | char *res = GNUNET_malloc (n); | ||
364 | char *enc; | ||
365 | unsigned int iparam = 0; | 365 | unsigned int iparam = 0; |
366 | size_t len = 0; | ||
367 | va_list args2; | ||
366 | 368 | ||
367 | grow_string (&res, proto, &n); | 369 | len += strlen (proto) + strlen( "://") + strlen (host); |
368 | grow_string (&res, "://", &n); | 370 | len += strlen (prefix) + strlen (path); |
369 | grow_string (&res, host, &n); | ||
370 | 371 | ||
371 | grow_string_path (&res, prefix, &n); | 372 | va_copy (args2, args); |
373 | while (1) | ||
374 | { | ||
375 | char *key; | ||
376 | char *value; | ||
377 | key = va_arg (args2, char *); | ||
378 | if (NULL == key) | ||
379 | break; | ||
380 | value = va_arg (args2, char *); | ||
381 | if (NULL == value) | ||
382 | continue; | ||
383 | len += urlencode_len (value) + strlen (key) + 2; | ||
384 | } | ||
385 | va_end (args2); | ||
386 | |||
387 | TALER_buffer_prealloc (&buf, len); | ||
372 | 388 | ||
373 | grow_string_path (&res, path, &n); | 389 | TALER_buffer_write_str (&buf, proto); |
390 | TALER_buffer_write_str (&buf, "://"); | ||
391 | TALER_buffer_write_str (&buf, host); | ||
374 | 392 | ||
393 | TALER_buffer_write_path (&buf, prefix); | ||
394 | TALER_buffer_write_path (&buf, path); | ||
395 | |||
396 | va_copy (args2, args); | ||
375 | while (1) | 397 | while (1) |
376 | { | 398 | { |
377 | char *key; | 399 | char *key; |
@@ -382,16 +404,15 @@ url_absolute_raw_va (const char *proto, | |||
382 | value = va_arg (args, char *); | 404 | value = va_arg (args, char *); |
383 | if (NULL == value) | 405 | if (NULL == value) |
384 | continue; | 406 | continue; |
385 | grow_string (&res, (0 == iparam) ? "?" : "&", &n); | 407 | TALER_buffer_write_str (&buf, (0 == iparam) ? "?" : "&"); |
386 | iparam++; | 408 | iparam++; |
387 | grow_string (&res, key, &n); | 409 | TALER_buffer_write_str (&buf, key); |
388 | grow_string (&res, "=", &n); | 410 | TALER_buffer_write_str (&buf, "="); |
389 | enc = TALER_urlencode (value); | 411 | buffer_write_urlencode (&buf, value); |
390 | grow_string (&res, enc, &n); | ||
391 | GNUNET_free (enc); | ||
392 | } | 412 | } |
413 | va_end (args2); | ||
393 | 414 | ||
394 | return res; | 415 | return TALER_buffer_reap_str (&buf); |
395 | } | 416 | } |
396 | 417 | ||
397 | 418 | ||
@@ -417,7 +438,7 @@ TALER_url_absolute_raw (const char *proto, | |||
417 | va_list args; | 438 | va_list args; |
418 | 439 | ||
419 | va_start (args, path); | 440 | va_start (args, path); |
420 | result = url_absolute_raw_va (proto, host, prefix, path, args); | 441 | result = TALER_url_absolute_raw_va (proto, host, prefix, path, args); |
421 | va_end (args); | 442 | va_end (args); |
422 | return result; | 443 | return result; |
423 | } | 444 | } |
@@ -516,10 +537,213 @@ TALER_url_absolute_mhd (struct MHD_Connection *connection, | |||
516 | } | 537 | } |
517 | 538 | ||
518 | va_start (args, path); | 539 | va_start (args, path); |
519 | result = url_absolute_raw_va (proto, host, prefix, path, args); | 540 | result = TALER_url_absolute_raw_va (proto, host, prefix, path, args); |
520 | va_end (args); | 541 | va_end (args); |
521 | return result; | 542 | return result; |
522 | } | 543 | } |
523 | 544 | ||
524 | 545 | ||
546 | /** | ||
547 | * Initialize a buffer with the given capacity. | ||
548 | * | ||
549 | * When a buffer is allocated with this function, a warning is logged | ||
550 | * when the buffer exceeds the initial capacity. | ||
551 | * | ||
552 | * @param buf the buffer to initialize | ||
553 | * @param capacity the capacity (in bytes) to allocate for @a buf | ||
554 | */ | ||
555 | void | ||
556 | TALER_buffer_prealloc (struct TALER_Buffer *buf, size_t capacity) | ||
557 | { | ||
558 | /* Buffer should be zero-initialized */ | ||
559 | GNUNET_assert (0 == buf->mem); | ||
560 | GNUNET_assert (0 == buf->capacity); | ||
561 | GNUNET_assert (0 == buf->position); | ||
562 | buf->mem = GNUNET_malloc (capacity); | ||
563 | buf->capacity = capacity; | ||
564 | buf->warn_grow = GNUNET_YES; | ||
565 | } | ||
566 | |||
567 | |||
568 | /** | ||
569 | * Make sure that at least @a n bytes remaining in the buffer. | ||
570 | * | ||
571 | * @param buf buffer to potentially grow | ||
572 | * @param n number of bytes that should be available to write | ||
573 | */ | ||
574 | void | ||
575 | TALER_buffer_ensure_remaining (struct TALER_Buffer *buf, size_t n) | ||
576 | { | ||
577 | size_t new_capacity = buf->position + n; | ||
578 | |||
579 | if (new_capacity <= buf->capacity) | ||
580 | return; | ||
581 | /* warn if calculation of expected size was wrong */ | ||
582 | GNUNET_break (GNUNET_YES != buf->warn_grow); | ||
583 | if (new_capacity < buf->capacity * 2) | ||
584 | new_capacity = buf->capacity * 2; | ||
585 | buf->capacity = new_capacity; | ||
586 | if (NULL != buf->mem) | ||
587 | buf->mem = GNUNET_realloc (buf->mem, new_capacity); | ||
588 | else | ||
589 | buf->mem = GNUNET_malloc (new_capacity); | ||
590 | } | ||
591 | |||
592 | |||
593 | /** | ||
594 | * Write bytes to the buffer. | ||
595 | * | ||
596 | * Grows the buffer if necessary. | ||
597 | * | ||
598 | * @param buf buffer to write to | ||
599 | * @param data data to read from | ||
600 | * @param len number of bytes to copy from @a data to @a buf | ||
601 | * | ||
602 | */ | ||
603 | void | ||
604 | TALER_buffer_write (struct TALER_Buffer *buf, const char *data, size_t len) | ||
605 | { | ||
606 | TALER_buffer_ensure_remaining (buf, len); | ||
607 | memcpy (buf->mem + buf->position, data, len); | ||
608 | buf->position += len; | ||
609 | } | ||
610 | |||
611 | |||
612 | /** | ||
613 | * Write a 0-terminated string to a buffer, excluding the 0-terminator. | ||
614 | * | ||
615 | * @param buf the buffer to write to | ||
616 | * @param str the string to write to @a buf | ||
617 | */ | ||
618 | void | ||
619 | TALER_buffer_write_str (struct TALER_Buffer *buf, const char *str) | ||
620 | { | ||
621 | size_t len = strlen (str); | ||
622 | |||
623 | TALER_buffer_write (buf, str, len); | ||
624 | } | ||
625 | |||
626 | |||
627 | /** | ||
628 | * Clear the buffer and return the string it contained. | ||
629 | * The caller is responsible to eventually #GNUNET_free | ||
630 | * the returned string. | ||
631 | * | ||
632 | * The returned string is always 0-terminated. | ||
633 | * | ||
634 | * @param buf the buffer to reap the string from | ||
635 | * @returns the buffer contained in the string | ||
636 | */ | ||
637 | char * | ||
638 | TALER_buffer_reap_str (struct TALER_Buffer *buf) | ||
639 | { | ||
640 | char *res; | ||
641 | |||
642 | /* ensure 0-termination */ | ||
643 | if ( (0 == buf->position) || ('\0' != buf->mem[buf->position - 1])) | ||
644 | { | ||
645 | TALER_buffer_ensure_remaining (buf, 1); | ||
646 | buf->mem[buf->position++] = '\0'; | ||
647 | } | ||
648 | res = buf->mem; | ||
649 | *buf = (struct TALER_Buffer) { 0 }; | ||
650 | return res; | ||
651 | } | ||
652 | |||
653 | |||
654 | /** | ||
655 | * Free the backing memory of the given buffer. | ||
656 | * Does not free the memory of the buffer control structure, | ||
657 | * which is typically stack-allocated. | ||
658 | */ | ||
659 | void | ||
660 | TALER_buffer_clear (struct TALER_Buffer *buf) | ||
661 | { | ||
662 | GNUNET_free_non_null (buf->mem); | ||
663 | *buf = (struct TALER_Buffer) { 0 }; | ||
664 | } | ||
665 | |||
666 | |||
667 | /** | ||
668 | * Write a path component to a buffer, ensuring that | ||
669 | * there is exactly one slash between the previous contents | ||
670 | * of the buffer and the new string. | ||
671 | * | ||
672 | * @param buf buffer to write to | ||
673 | * @param str string containing the new path component | ||
674 | */ | ||
675 | void | ||
676 | TALER_buffer_write_path (struct TALER_Buffer *buf, const char *str) | ||
677 | { | ||
678 | size_t len = strlen (str); | ||
679 | |||
680 | if (0 == len) | ||
681 | return; | ||
682 | if ('/' == str[0]) | ||
683 | { | ||
684 | str++; | ||
685 | len--; | ||
686 | } | ||
687 | if ( (0 == buf->position) || ('/' != buf->mem[buf->position]) ) | ||
688 | { | ||
689 | TALER_buffer_ensure_remaining (buf, 1); | ||
690 | buf->mem[buf->position++] = '/'; | ||
691 | } | ||
692 | TALER_buffer_write (buf, str, len); | ||
693 | } | ||
694 | |||
695 | |||
696 | /** | ||
697 | * Write a 0-terminated formatted string to a buffer, excluding the | ||
698 | * 0-terminator. | ||
699 | * | ||
700 | * Grows the buffer if necessary. | ||
701 | * | ||
702 | * @param buf the buffer to write to | ||
703 | * @param fmt format string | ||
704 | * @param ... format arguments | ||
705 | */ | ||
706 | void | ||
707 | TALER_buffer_write_fstr (struct TALER_Buffer *buf, const char *fmt, ...) | ||
708 | { | ||
709 | va_list args; | ||
710 | |||
711 | va_start (args, fmt); | ||
712 | TALER_buffer_write_vfstr (buf, fmt, args); | ||
713 | va_end (args); | ||
714 | } | ||
715 | |||
716 | |||
717 | /** | ||
718 | * Write a 0-terminated formatted string to a buffer, excluding the | ||
719 | * 0-terminator. | ||
720 | * | ||
721 | * Grows the buffer if necessary. | ||
722 | * | ||
723 | * @param buf the buffer to write to | ||
724 | * @param fmt format string | ||
725 | * @param args format argument list | ||
726 | */ | ||
727 | void | ||
728 | TALER_buffer_write_vfstr (struct TALER_Buffer *buf, const char *fmt, va_list args) | ||
729 | { | ||
730 | size_t res; | ||
731 | va_list args2; | ||
732 | |||
733 | va_copy (args2, args); | ||
734 | res = vsnprintf (NULL, 0, fmt, args2); | ||
735 | va_end (args2); | ||
736 | |||
737 | GNUNET_assert (res >= 0); | ||
738 | TALER_buffer_ensure_remaining (buf, res + 1); | ||
739 | |||
740 | va_copy (args2, args); | ||
741 | res = vsnprintf (buf->mem + buf->position, res + 1, fmt, args2); | ||
742 | va_end (args2); | ||
743 | |||
744 | GNUNET_assert (res >= 0); | ||
745 | buf->position += res; | ||
746 | GNUNET_assert (buf->position <= buf->capacity); | ||
747 | } | ||
748 | |||
525 | /* end of util.c */ | 749 | /* end of util.c */ |