aboutsummaryrefslogtreecommitdiff
path: root/src/util/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/util.c')
-rw-r--r--src/util/util.c428
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 */
203static size_t
204urlencode_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 */
203char * 219static void
204TALER_urlencode (const char *s) 220buffer_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 */
240static char *
241grow_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 */
263static char * 240char *
264grow_string_path (char **s, const char *p, size_t *n) 241TALER_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 */
291char * 260char *
292TALER_url_join (const char *base_url, 261TALER_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 */
355char * 357char *
356url_absolute_raw_va (const char *proto, 358TALER_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 */
555void
556TALER_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 */
574void
575TALER_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 */
603void
604TALER_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 */
618void
619TALER_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 */
637char *
638TALER_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 */
659void
660TALER_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 */
675void
676TALER_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 */
706void
707TALER_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 */
727void
728TALER_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 */