diff options
Diffstat (limited to 'src/lib/util/strings.c')
-rw-r--r-- | src/lib/util/strings.c | 1994 |
1 files changed, 1994 insertions, 0 deletions
diff --git a/src/lib/util/strings.c b/src/lib/util/strings.c new file mode 100644 index 000000000..86323bc83 --- /dev/null +++ b/src/lib/util/strings.c | |||
@@ -0,0 +1,1994 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2005-2017 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | /** | ||
21 | * @file util/strings.c | ||
22 | * @brief string functions | ||
23 | * @author Nils Durner | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | |||
28 | #include "platform.h" | ||
29 | #if HAVE_ICONV | ||
30 | #include <iconv.h> | ||
31 | #endif | ||
32 | #include "gnunet_util_lib.h" | ||
33 | #include <unicase.h> | ||
34 | #include <unistr.h> | ||
35 | #include <uniconv.h> | ||
36 | |||
37 | #define LOG(kind, ...) GNUNET_log_from (kind, "util-strings", __VA_ARGS__) | ||
38 | |||
39 | #define LOG_STRERROR(kind, syscall) \ | ||
40 | GNUNET_log_from_strerror (kind, "util-strings", syscall) | ||
41 | |||
42 | |||
43 | size_t | ||
44 | GNUNET_STRINGS_buffer_fill (char *buffer, | ||
45 | size_t size, | ||
46 | unsigned int count, ...) | ||
47 | { | ||
48 | size_t needed; | ||
49 | va_list ap; | ||
50 | |||
51 | needed = 0; | ||
52 | va_start (ap, count); | ||
53 | while (count > 0) | ||
54 | { | ||
55 | const char *s = va_arg (ap, const char *); | ||
56 | size_t slen = strlen (s) + 1; | ||
57 | |||
58 | GNUNET_assert (slen <= size - needed); | ||
59 | if (NULL != buffer) | ||
60 | GNUNET_memcpy (&buffer[needed], | ||
61 | s, | ||
62 | slen); | ||
63 | needed += slen; | ||
64 | count--; | ||
65 | } | ||
66 | va_end (ap); | ||
67 | return needed; | ||
68 | } | ||
69 | |||
70 | |||
71 | unsigned int | ||
72 | GNUNET_STRINGS_buffer_tokenize (const char *buffer, | ||
73 | size_t size, | ||
74 | unsigned int count, | ||
75 | ...) | ||
76 | { | ||
77 | unsigned int start; | ||
78 | unsigned int needed; | ||
79 | const char **r; | ||
80 | va_list ap; | ||
81 | |||
82 | needed = 0; | ||
83 | va_start (ap, count); | ||
84 | while (count > 0) | ||
85 | { | ||
86 | r = va_arg (ap, const char **); | ||
87 | |||
88 | start = needed; | ||
89 | while ((needed < size) && (buffer[needed] != '\0')) | ||
90 | needed++; | ||
91 | if (needed == size) | ||
92 | { | ||
93 | va_end (ap); | ||
94 | return 0; /* error */ | ||
95 | } | ||
96 | *r = &buffer[start]; | ||
97 | needed++; /* skip 0-termination */ | ||
98 | count--; | ||
99 | } | ||
100 | va_end (ap); | ||
101 | return needed; | ||
102 | } | ||
103 | |||
104 | |||
105 | char * | ||
106 | GNUNET_STRINGS_byte_size_fancy (unsigned long long size) | ||
107 | { | ||
108 | const char *unit = /* size unit */ "b"; | ||
109 | char *ret; | ||
110 | |||
111 | if (size > 5 * 1024) | ||
112 | { | ||
113 | size = size / 1024; | ||
114 | unit = "KiB"; | ||
115 | if (size > 5 * 1024) | ||
116 | { | ||
117 | size = size / 1024; | ||
118 | unit = "MiB"; | ||
119 | if (size > 5 * 1024) | ||
120 | { | ||
121 | size = size / 1024; | ||
122 | unit = "GiB"; | ||
123 | if (size > 5 * 1024) | ||
124 | { | ||
125 | size = size / 1024; | ||
126 | unit = "TiB"; | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | ret = GNUNET_malloc (32); | ||
132 | GNUNET_snprintf (ret, 32, "%llu %s", size, unit); | ||
133 | return ret; | ||
134 | } | ||
135 | |||
136 | |||
137 | size_t | ||
138 | GNUNET_strlcpy (char *dst, | ||
139 | const char *src, | ||
140 | size_t n) | ||
141 | { | ||
142 | size_t slen; | ||
143 | |||
144 | GNUNET_assert (0 != n); | ||
145 | slen = strnlen (src, n - 1); | ||
146 | memcpy (dst, src, slen); | ||
147 | dst[slen] = '\0'; | ||
148 | return slen; | ||
149 | } | ||
150 | |||
151 | |||
152 | /** | ||
153 | * Unit conversion table entry for 'convert_with_table'. | ||
154 | */ | ||
155 | struct ConversionTable | ||
156 | { | ||
157 | /** | ||
158 | * Name of the unit (or NULL for end of table). | ||
159 | */ | ||
160 | const char *name; | ||
161 | |||
162 | /** | ||
163 | * Factor to apply for this unit. | ||
164 | */ | ||
165 | unsigned long long value; | ||
166 | }; | ||
167 | |||
168 | |||
169 | /** | ||
170 | * Convert a string of the form "4 X 5 Y" into a numeric value | ||
171 | * by interpreting "X" and "Y" as units and then multiplying | ||
172 | * the numbers with the values associated with the respective | ||
173 | * unit from the conversion table. | ||
174 | * | ||
175 | * @param input input string to parse | ||
176 | * @param table table with the conversion of unit names to numbers | ||
177 | * @param output where to store the result | ||
178 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error | ||
179 | */ | ||
180 | static enum GNUNET_GenericReturnValue | ||
181 | convert_with_table (const char *input, | ||
182 | const struct ConversionTable *table, | ||
183 | unsigned long long *output) | ||
184 | { | ||
185 | unsigned long long ret; | ||
186 | char *in; | ||
187 | const char *tok; | ||
188 | unsigned long long last; | ||
189 | unsigned int i; | ||
190 | char *sptr; | ||
191 | |||
192 | ret = 0; | ||
193 | last = 0; | ||
194 | in = GNUNET_strdup (input); | ||
195 | for (tok = strtok_r (in, " ", &sptr); | ||
196 | tok != NULL; | ||
197 | tok = strtok_r (NULL, " ", &sptr)) | ||
198 | { | ||
199 | do | ||
200 | { | ||
201 | i = 0; | ||
202 | while ((table[i].name != NULL) && (0 != strcasecmp (table[i].name, tok))) | ||
203 | i++; | ||
204 | if (table[i].name != NULL) | ||
205 | { | ||
206 | last *= table[i].value; | ||
207 | break; /* next tok */ | ||
208 | } | ||
209 | else | ||
210 | { | ||
211 | char *endptr; | ||
212 | ret += last; | ||
213 | errno = 0; | ||
214 | last = strtoull (tok, &endptr, 10); | ||
215 | if ((0 != errno) || (endptr == tok)) | ||
216 | { | ||
217 | GNUNET_free (in); | ||
218 | return GNUNET_SYSERR; /* expected number */ | ||
219 | } | ||
220 | if ('\0' == endptr[0]) | ||
221 | break; /* next tok */ | ||
222 | else | ||
223 | tok = endptr; /* and re-check (handles times like "10s") */ | ||
224 | } | ||
225 | } | ||
226 | while (GNUNET_YES); | ||
227 | } | ||
228 | ret += last; | ||
229 | *output = ret; | ||
230 | GNUNET_free (in); | ||
231 | return GNUNET_OK; | ||
232 | } | ||
233 | |||
234 | |||
235 | enum GNUNET_GenericReturnValue | ||
236 | GNUNET_STRINGS_fancy_size_to_bytes (const char *fancy_size, | ||
237 | unsigned long long *size) | ||
238 | { | ||
239 | static const struct ConversionTable table[] = | ||
240 | { { "B", 1 }, | ||
241 | { "KiB", 1024 }, | ||
242 | { "kB", 1000 }, | ||
243 | { "MiB", 1024 * 1024 }, | ||
244 | { "MB", 1000 * 1000 }, | ||
245 | { "GiB", 1024 * 1024 * 1024 }, | ||
246 | { "GB", 1000 * 1000 * 1000 }, | ||
247 | { "TiB", 1024LL * 1024LL * 1024LL * 1024LL }, | ||
248 | { "TB", 1000LL * 1000LL * 1000LL * 1024LL }, | ||
249 | { "PiB", 1024LL * 1024LL * 1024LL * 1024LL * 1024LL }, | ||
250 | { "PB", 1000LL * 1000LL * 1000LL * 1024LL * 1000LL }, | ||
251 | { "EiB", 1024LL * 1024LL * 1024LL * 1024LL * 1024LL * 1024LL }, | ||
252 | { "EB", 1000LL * 1000LL * 1000LL * 1024LL * 1000LL * 1000LL }, | ||
253 | { NULL, 0 } }; | ||
254 | |||
255 | return convert_with_table (fancy_size, table, size); | ||
256 | } | ||
257 | |||
258 | |||
259 | enum GNUNET_GenericReturnValue | ||
260 | GNUNET_STRINGS_fancy_time_to_relative (const char *fancy_time, | ||
261 | struct GNUNET_TIME_Relative *rtime) | ||
262 | { | ||
263 | static const struct ConversionTable table[] = | ||
264 | { { "us", 1 }, | ||
265 | { "ms", 1000 }, | ||
266 | { "s", 1000 * 1000LL }, | ||
267 | { "second", 1000 * 1000LL }, | ||
268 | { "seconds", 1000 * 1000LL }, | ||
269 | { "\"", 1000 * 1000LL }, | ||
270 | { "m", 60 * 1000 * 1000LL }, | ||
271 | { "min", 60 * 1000 * 1000LL }, | ||
272 | { "minute", 60 * 1000 * 1000LL }, | ||
273 | { "minutes", 60 * 1000 * 1000LL }, | ||
274 | { "'", 60 * 1000 * 1000LL }, | ||
275 | { "h", 60 * 60 * 1000 * 1000LL }, | ||
276 | { "hour", 60 * 60 * 1000 * 1000LL }, | ||
277 | { "hours", 60 * 60 * 1000 * 1000LL }, | ||
278 | { "d", 24 * 60 * 60 * 1000LL * 1000LL }, | ||
279 | { "day", 24 * 60 * 60 * 1000LL * 1000LL }, | ||
280 | { "days", 24 * 60 * 60 * 1000LL * 1000LL }, | ||
281 | { "week", 7 * 24 * 60 * 60 * 1000LL * 1000LL }, | ||
282 | { "weeks", 7 * 24 * 60 * 60 * 1000LL * 1000LL }, | ||
283 | { "year", 31536000000000LL /* year */ }, | ||
284 | { "years", 31536000000000LL /* year */ }, | ||
285 | { "a", 31536000000000LL /* year */ }, | ||
286 | { NULL, 0 } }; | ||
287 | int ret; | ||
288 | unsigned long long val; | ||
289 | |||
290 | if (0 == strcasecmp ("forever", fancy_time)) | ||
291 | { | ||
292 | *rtime = GNUNET_TIME_UNIT_FOREVER_REL; | ||
293 | return GNUNET_OK; | ||
294 | } | ||
295 | ret = convert_with_table (fancy_time, table, &val); | ||
296 | rtime->rel_value_us = (uint64_t) val; | ||
297 | return ret; | ||
298 | } | ||
299 | |||
300 | |||
301 | enum GNUNET_GenericReturnValue | ||
302 | GNUNET_STRINGS_fancy_time_to_absolute (const char *fancy_time, | ||
303 | struct GNUNET_TIME_Absolute *atime) | ||
304 | { | ||
305 | struct tm tv; | ||
306 | time_t t; | ||
307 | const char *eos; | ||
308 | |||
309 | if (0 == strcasecmp ("end of time", fancy_time)) | ||
310 | { | ||
311 | *atime = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
312 | return GNUNET_OK; | ||
313 | } | ||
314 | eos = &fancy_time[strlen (fancy_time)]; | ||
315 | memset (&tv, 0, sizeof(tv)); | ||
316 | if ((eos != strptime (fancy_time, "%a %b %d %H:%M:%S %Y", &tv)) && | ||
317 | (eos != strptime (fancy_time, "%c", &tv)) && | ||
318 | (eos != strptime (fancy_time, "%Ec", &tv)) && | ||
319 | (eos != strptime (fancy_time, "%Y-%m-%d %H:%M:%S", &tv)) && | ||
320 | (eos != strptime (fancy_time, "%Y-%m-%d %H:%M", &tv)) && | ||
321 | (eos != strptime (fancy_time, "%x", &tv)) && | ||
322 | (eos != strptime (fancy_time, "%Ex", &tv)) && | ||
323 | (eos != strptime (fancy_time, "%Y-%m-%d", &tv)) && | ||
324 | (eos != strptime (fancy_time, "%Y-%m", &tv)) && | ||
325 | (eos != strptime (fancy_time, "%Y", &tv))) | ||
326 | return GNUNET_SYSERR; | ||
327 | t = mktime (&tv); | ||
328 | atime->abs_value_us = (uint64_t) ((uint64_t) t * 1000LL * 1000LL); | ||
329 | return GNUNET_OK; | ||
330 | } | ||
331 | |||
332 | |||
333 | enum GNUNET_GenericReturnValue | ||
334 | GNUNET_STRINGS_fancy_time_to_timestamp (const char *fancy_time, | ||
335 | struct GNUNET_TIME_Timestamp *atime) | ||
336 | { | ||
337 | return GNUNET_STRINGS_fancy_time_to_absolute (fancy_time, | ||
338 | &atime->abs_time); | ||
339 | } | ||
340 | |||
341 | |||
342 | char * | ||
343 | GNUNET_STRINGS_conv (const char *input, | ||
344 | size_t len, | ||
345 | const char *input_charset, | ||
346 | const char *output_charset) | ||
347 | { | ||
348 | char *ret; | ||
349 | uint8_t *u8_string; | ||
350 | char *encoded_string; | ||
351 | size_t u8_string_length; | ||
352 | size_t encoded_string_length; | ||
353 | |||
354 | u8_string = u8_conv_from_encoding (input_charset, | ||
355 | iconveh_error, | ||
356 | input, | ||
357 | len, | ||
358 | NULL, | ||
359 | NULL, | ||
360 | &u8_string_length); | ||
361 | if (NULL == u8_string) | ||
362 | { | ||
363 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "u8_conv_from_encoding"); | ||
364 | goto fail; | ||
365 | } | ||
366 | if (0 == strcmp (output_charset, "UTF-8")) | ||
367 | { | ||
368 | ret = GNUNET_malloc (u8_string_length + 1); | ||
369 | GNUNET_memcpy (ret, u8_string, u8_string_length); | ||
370 | ret[u8_string_length] = '\0'; | ||
371 | free (u8_string); | ||
372 | return ret; | ||
373 | } | ||
374 | encoded_string = u8_conv_to_encoding (output_charset, | ||
375 | iconveh_error, | ||
376 | u8_string, | ||
377 | u8_string_length, | ||
378 | NULL, | ||
379 | NULL, | ||
380 | &encoded_string_length); | ||
381 | free (u8_string); | ||
382 | if (NULL == encoded_string) | ||
383 | { | ||
384 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "u8_conv_to_encoding"); | ||
385 | goto fail; | ||
386 | } | ||
387 | ret = GNUNET_malloc (encoded_string_length + 1); | ||
388 | GNUNET_memcpy (ret, encoded_string, encoded_string_length); | ||
389 | ret[encoded_string_length] = '\0'; | ||
390 | free (encoded_string); | ||
391 | return ret; | ||
392 | fail: | ||
393 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
394 | _ ("Character sets requested were `%s'->`%s'\n"), | ||
395 | "UTF-8", | ||
396 | output_charset); | ||
397 | ret = GNUNET_malloc (len + 1); | ||
398 | GNUNET_memcpy (ret, input, len); | ||
399 | ret[len] = '\0'; | ||
400 | return ret; | ||
401 | } | ||
402 | |||
403 | |||
404 | char * | ||
405 | GNUNET_STRINGS_to_utf8 (const char *input, | ||
406 | size_t len, | ||
407 | const char *charset) | ||
408 | { | ||
409 | return GNUNET_STRINGS_conv (input, | ||
410 | len, | ||
411 | charset, | ||
412 | "UTF-8"); | ||
413 | } | ||
414 | |||
415 | |||
416 | char * | ||
417 | GNUNET_STRINGS_from_utf8 (const char *input, | ||
418 | size_t len, | ||
419 | const char *charset) | ||
420 | { | ||
421 | return GNUNET_STRINGS_conv (input, | ||
422 | len, | ||
423 | "UTF-8", | ||
424 | charset); | ||
425 | } | ||
426 | |||
427 | |||
428 | char * | ||
429 | GNUNET_STRINGS_utf8_normalize (const char *input) | ||
430 | { | ||
431 | uint8_t *tmp; | ||
432 | size_t len; | ||
433 | char *output; | ||
434 | tmp = u8_normalize (UNINORM_NFC, | ||
435 | (uint8_t *) input, | ||
436 | strlen ((char*) input), | ||
437 | NULL, | ||
438 | &len); | ||
439 | if (NULL == tmp) | ||
440 | return NULL; | ||
441 | output = GNUNET_malloc (len + 1); | ||
442 | GNUNET_memcpy (output, tmp, len); | ||
443 | output[len] = '\0'; | ||
444 | free (tmp); | ||
445 | return output; | ||
446 | } | ||
447 | |||
448 | enum GNUNET_GenericReturnValue | ||
449 | GNUNET_STRINGS_utf8_tolower (const char *input, | ||
450 | char *output) | ||
451 | { | ||
452 | uint8_t *tmp_in; | ||
453 | size_t len; | ||
454 | |||
455 | tmp_in = u8_tolower ((uint8_t *) input, | ||
456 | strlen ((char *) input), | ||
457 | NULL, | ||
458 | UNINORM_NFD, | ||
459 | NULL, | ||
460 | &len); | ||
461 | if (NULL == tmp_in) | ||
462 | return GNUNET_SYSERR; | ||
463 | GNUNET_memcpy (output, tmp_in, len); | ||
464 | output[len] = '\0'; | ||
465 | GNUNET_free (tmp_in); | ||
466 | return GNUNET_OK; | ||
467 | } | ||
468 | |||
469 | |||
470 | enum GNUNET_GenericReturnValue | ||
471 | GNUNET_STRINGS_utf8_toupper (const char *input, | ||
472 | char *output) | ||
473 | { | ||
474 | uint8_t *tmp_in; | ||
475 | size_t len; | ||
476 | |||
477 | tmp_in = u8_toupper ((uint8_t *) input, | ||
478 | strlen ((char *) input), | ||
479 | NULL, | ||
480 | UNINORM_NFD, | ||
481 | NULL, | ||
482 | &len); | ||
483 | if (NULL == tmp_in) | ||
484 | return GNUNET_SYSERR; | ||
485 | /* 0-terminator does not fit */ | ||
486 | GNUNET_memcpy (output, tmp_in, len); | ||
487 | output[len] = '\0'; | ||
488 | GNUNET_free (tmp_in); | ||
489 | return GNUNET_OK; | ||
490 | } | ||
491 | |||
492 | |||
493 | char * | ||
494 | GNUNET_STRINGS_filename_expand (const char *fil) | ||
495 | { | ||
496 | char *buffer; | ||
497 | size_t len; | ||
498 | char *fm; | ||
499 | const char *fil_ptr; | ||
500 | |||
501 | if (fil == NULL) | ||
502 | return NULL; | ||
503 | |||
504 | if (fil[0] == DIR_SEPARATOR) | ||
505 | /* absolute path, just copy */ | ||
506 | return GNUNET_strdup (fil); | ||
507 | if (fil[0] == '~') | ||
508 | { | ||
509 | fm = getenv ("HOME"); | ||
510 | if (fm == NULL) | ||
511 | { | ||
512 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
513 | _ ("Failed to expand `$HOME': environment variable `HOME' not set")); | ||
514 | return NULL; | ||
515 | } | ||
516 | fm = GNUNET_strdup (fm); | ||
517 | /* do not copy '~' */ | ||
518 | fil_ptr = fil + 1; | ||
519 | |||
520 | /* skip over dir separator to be consistent */ | ||
521 | if (fil_ptr[0] == DIR_SEPARATOR) | ||
522 | fil_ptr++; | ||
523 | } | ||
524 | else | ||
525 | { | ||
526 | /* relative path */ | ||
527 | fil_ptr = fil; | ||
528 | len = 512; | ||
529 | fm = NULL; | ||
530 | while (1) | ||
531 | { | ||
532 | buffer = GNUNET_malloc (len); | ||
533 | if (getcwd (buffer, len) != NULL) | ||
534 | { | ||
535 | fm = buffer; | ||
536 | break; | ||
537 | } | ||
538 | if ((errno == ERANGE) && (len < 1024 * 1024 * 4)) | ||
539 | { | ||
540 | len *= 2; | ||
541 | GNUNET_free (buffer); | ||
542 | continue; | ||
543 | } | ||
544 | GNUNET_free (buffer); | ||
545 | break; | ||
546 | } | ||
547 | if (fm == NULL) | ||
548 | { | ||
549 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "getcwd"); | ||
550 | buffer = getenv ("PWD"); /* alternative */ | ||
551 | if (buffer != NULL) | ||
552 | fm = GNUNET_strdup (buffer); | ||
553 | } | ||
554 | if (fm == NULL) | ||
555 | fm = GNUNET_strdup ("./"); /* give up */ | ||
556 | } | ||
557 | GNUNET_asprintf (&buffer, | ||
558 | "%s%s%s", | ||
559 | fm, | ||
560 | (fm[strlen (fm) - 1] == DIR_SEPARATOR) ? "" | ||
561 | : DIR_SEPARATOR_STR, | ||
562 | fil_ptr); | ||
563 | GNUNET_free (fm); | ||
564 | return buffer; | ||
565 | } | ||
566 | |||
567 | |||
568 | const char * | ||
569 | GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative delta, | ||
570 | int do_round) | ||
571 | { | ||
572 | static GNUNET_THREAD_LOCAL char buf[128]; | ||
573 | const char *unit = /* time unit */ "µs"; | ||
574 | uint64_t dval = delta.rel_value_us; | ||
575 | |||
576 | if (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us == delta.rel_value_us) | ||
577 | return "forever"; | ||
578 | if (0 == delta.rel_value_us) | ||
579 | return "0 ms"; | ||
580 | if (((GNUNET_YES == do_round) && (dval > 5 * 1000)) || (0 == (dval % 1000))) | ||
581 | { | ||
582 | dval = dval / 1000; | ||
583 | unit = /* time unit */ "ms"; | ||
584 | if (((GNUNET_YES == do_round) && (dval > 5 * 1000)) || (0 == (dval % 1000))) | ||
585 | { | ||
586 | dval = dval / 1000; | ||
587 | unit = /* time unit */ "s"; | ||
588 | if (((GNUNET_YES == do_round) && (dval > 5 * 60)) || (0 == (dval % 60))) | ||
589 | { | ||
590 | dval = dval / 60; | ||
591 | unit = /* time unit */ "m"; | ||
592 | if (((GNUNET_YES == do_round) && (dval > 5 * 60)) || (0 == (dval % 60))) | ||
593 | { | ||
594 | dval = dval / 60; | ||
595 | unit = /* time unit */ "h"; | ||
596 | if (((GNUNET_YES == do_round) && (dval > 5 * 24)) || | ||
597 | (0 == (dval % 24))) | ||
598 | { | ||
599 | dval = dval / 24; | ||
600 | if (1 == dval) | ||
601 | unit = /* time unit */ "day"; | ||
602 | else | ||
603 | unit = /* time unit */ "days"; | ||
604 | } | ||
605 | } | ||
606 | } | ||
607 | } | ||
608 | } | ||
609 | GNUNET_snprintf (buf, sizeof(buf), "%llu %s", | ||
610 | (unsigned long long) dval, unit); | ||
611 | return buf; | ||
612 | } | ||
613 | |||
614 | |||
615 | const char * | ||
616 | GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t) | ||
617 | { | ||
618 | static GNUNET_THREAD_LOCAL char buf[255]; | ||
619 | time_t tt; | ||
620 | struct tm *tp; | ||
621 | |||
622 | if (GNUNET_TIME_absolute_is_never (t)) | ||
623 | return "end of time"; | ||
624 | tt = t.abs_value_us / 1000LL / 1000LL; | ||
625 | tp = localtime (&tt); | ||
626 | /* This is hacky, but i don't know a way to detect libc character encoding. | ||
627 | * Just expect utf8 from glibc these days. | ||
628 | * As for msvcrt, use the wide variant, which always returns utf16 | ||
629 | * (otherwise we'd have to detect current codepage or use W32API character | ||
630 | * set conversion routines to convert to UTF8). | ||
631 | */ | ||
632 | strftime (buf, sizeof(buf), "%a %b %d %H:%M:%S %Y", tp); | ||
633 | |||
634 | return buf; | ||
635 | } | ||
636 | |||
637 | |||
638 | const char * | ||
639 | GNUNET_STRINGS_get_short_name (const char *filename) | ||
640 | { | ||
641 | const char *short_fn = filename; | ||
642 | const char *ss; | ||
643 | |||
644 | while (NULL != (ss = strstr (short_fn, DIR_SEPARATOR_STR)) && (ss[1] != '\0')) | ||
645 | short_fn = 1 + ss; | ||
646 | return short_fn; | ||
647 | } | ||
648 | |||
649 | |||
650 | /** | ||
651 | * Get the decoded value corresponding to a character according to Crockford | ||
652 | * Base32 encoding. | ||
653 | * | ||
654 | * @param a a character | ||
655 | * @return corresponding numeric value | ||
656 | */ | ||
657 | static unsigned int | ||
658 | getValue__ (unsigned char a) | ||
659 | { | ||
660 | unsigned int dec; | ||
661 | |||
662 | switch (a) | ||
663 | { | ||
664 | case 'O': | ||
665 | case 'o': | ||
666 | a = '0'; | ||
667 | break; | ||
668 | |||
669 | case 'i': | ||
670 | case 'I': | ||
671 | case 'l': | ||
672 | case 'L': | ||
673 | a = '1'; | ||
674 | break; | ||
675 | |||
676 | /* also consider U to be V */ | ||
677 | case 'u': | ||
678 | case 'U': | ||
679 | a = 'V'; | ||
680 | break; | ||
681 | |||
682 | default: | ||
683 | break; | ||
684 | } | ||
685 | if ((a >= '0') && (a <= '9')) | ||
686 | return a - '0'; | ||
687 | if ((a >= 'a') && (a <= 'z')) | ||
688 | a = toupper (a); | ||
689 | /* return (a - 'a' + 10); */ | ||
690 | dec = 0; | ||
691 | if ((a >= 'A') && (a <= 'Z')) | ||
692 | { | ||
693 | if ('I' < a) | ||
694 | dec++; | ||
695 | if ('L' < a) | ||
696 | dec++; | ||
697 | if ('O' < a) | ||
698 | dec++; | ||
699 | if ('U' < a) | ||
700 | dec++; | ||
701 | return(a - 'A' + 10 - dec); | ||
702 | } | ||
703 | return -1; | ||
704 | } | ||
705 | |||
706 | |||
707 | char * | ||
708 | GNUNET_STRINGS_data_to_string (const void *data, | ||
709 | size_t size, | ||
710 | char *out, | ||
711 | size_t out_size) | ||
712 | { | ||
713 | /** | ||
714 | * 32 characters for encoding | ||
715 | */ | ||
716 | static char *encTable__ = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; | ||
717 | unsigned int wpos; | ||
718 | unsigned int rpos; | ||
719 | unsigned int bits; | ||
720 | unsigned int vbit; | ||
721 | const unsigned char *udata; | ||
722 | |||
723 | GNUNET_assert (size < SIZE_MAX / 8 - 4); | ||
724 | udata = data; | ||
725 | if (out_size < (size * 8 + 4) / 5) | ||
726 | { | ||
727 | GNUNET_break (0); | ||
728 | return NULL; | ||
729 | } | ||
730 | vbit = 0; | ||
731 | wpos = 0; | ||
732 | rpos = 0; | ||
733 | bits = 0; | ||
734 | while ((rpos < size) || (vbit > 0)) | ||
735 | { | ||
736 | if ((rpos < size) && (vbit < 5)) | ||
737 | { | ||
738 | bits = (bits << 8) | udata[rpos++]; /* eat 8 more bits */ | ||
739 | vbit += 8; | ||
740 | } | ||
741 | if (vbit < 5) | ||
742 | { | ||
743 | bits <<= (5 - vbit); /* zero-padding */ | ||
744 | GNUNET_assert (vbit == ((size * 8) % 5)); | ||
745 | vbit = 5; | ||
746 | } | ||
747 | if (wpos >= out_size) | ||
748 | { | ||
749 | GNUNET_break (0); | ||
750 | return NULL; | ||
751 | } | ||
752 | out[wpos++] = encTable__[(bits >> (vbit - 5)) & 31]; | ||
753 | vbit -= 5; | ||
754 | } | ||
755 | GNUNET_assert (0 == vbit); | ||
756 | if (wpos < out_size) | ||
757 | out[wpos] = '\0'; | ||
758 | return &out[wpos]; | ||
759 | } | ||
760 | |||
761 | |||
762 | char * | ||
763 | GNUNET_STRINGS_data_to_string_alloc (const void *buf, size_t size) | ||
764 | { | ||
765 | char *str_buf; | ||
766 | size_t len = size * 8; | ||
767 | char *end; | ||
768 | |||
769 | if (len % 5 > 0) | ||
770 | len += 5 - len % 5; | ||
771 | len /= 5; | ||
772 | str_buf = GNUNET_malloc (len + 1); | ||
773 | end = GNUNET_STRINGS_data_to_string (buf, | ||
774 | size, | ||
775 | str_buf, | ||
776 | len); | ||
777 | if (NULL == end) | ||
778 | { | ||
779 | GNUNET_free (str_buf); | ||
780 | return NULL; | ||
781 | } | ||
782 | *end = '\0'; | ||
783 | return str_buf; | ||
784 | } | ||
785 | |||
786 | |||
787 | enum GNUNET_GenericReturnValue | ||
788 | GNUNET_STRINGS_string_to_data (const char *enc, | ||
789 | size_t enclen, | ||
790 | void *out, | ||
791 | size_t out_size) | ||
792 | { | ||
793 | size_t rpos; | ||
794 | size_t wpos; | ||
795 | unsigned int bits; | ||
796 | unsigned int vbit; | ||
797 | int ret; | ||
798 | int shift; | ||
799 | unsigned char *uout; | ||
800 | size_t encoded_len; | ||
801 | |||
802 | if (0 == enclen) | ||
803 | { | ||
804 | if (0 == out_size) | ||
805 | return GNUNET_OK; | ||
806 | return GNUNET_SYSERR; | ||
807 | } | ||
808 | GNUNET_assert (out_size < SIZE_MAX / 8); | ||
809 | encoded_len = out_size * 8; | ||
810 | uout = out; | ||
811 | wpos = out_size; | ||
812 | rpos = enclen; | ||
813 | if ((encoded_len % 5) > 0) | ||
814 | { | ||
815 | vbit = encoded_len % 5; /* padding! */ | ||
816 | shift = 5 - vbit; | ||
817 | bits = (ret = getValue__ (enc[--rpos])) >> shift; | ||
818 | } | ||
819 | else | ||
820 | { | ||
821 | vbit = 5; | ||
822 | shift = 0; | ||
823 | bits = (ret = getValue__ (enc[--rpos])); | ||
824 | } | ||
825 | if ((encoded_len + shift) / 5 != enclen) | ||
826 | return GNUNET_SYSERR; | ||
827 | if (-1 == ret) | ||
828 | return GNUNET_SYSERR; | ||
829 | while (wpos > 0) | ||
830 | { | ||
831 | if (0 == rpos) | ||
832 | { | ||
833 | GNUNET_break (0); | ||
834 | return GNUNET_SYSERR; | ||
835 | } | ||
836 | bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits; | ||
837 | if (-1 == ret) | ||
838 | return GNUNET_SYSERR; | ||
839 | vbit += 5; | ||
840 | if (vbit >= 8) | ||
841 | { | ||
842 | uout[--wpos] = (unsigned char) bits; | ||
843 | bits >>= 8; | ||
844 | vbit -= 8; | ||
845 | } | ||
846 | } | ||
847 | if ((0 != rpos) || (0 != vbit)) | ||
848 | return GNUNET_SYSERR; | ||
849 | return GNUNET_OK; | ||
850 | } | ||
851 | |||
852 | |||
853 | enum GNUNET_GenericReturnValue | ||
854 | GNUNET_STRINGS_string_to_data_alloc (const char *enc, | ||
855 | size_t enclen, | ||
856 | void **out, | ||
857 | size_t *out_size) | ||
858 | { | ||
859 | size_t size; | ||
860 | void *data; | ||
861 | int res; | ||
862 | |||
863 | size = (enclen * 5) / 8; | ||
864 | if (size >= GNUNET_MAX_MALLOC_CHECKED) | ||
865 | { | ||
866 | GNUNET_break_op (0); | ||
867 | return GNUNET_SYSERR; | ||
868 | } | ||
869 | data = GNUNET_malloc (size); | ||
870 | res = GNUNET_STRINGS_string_to_data (enc, | ||
871 | enclen, | ||
872 | data, | ||
873 | size); | ||
874 | if ( (0 < size) && | ||
875 | (GNUNET_OK != res) ) | ||
876 | { | ||
877 | size--; | ||
878 | res = GNUNET_STRINGS_string_to_data (enc, | ||
879 | enclen, | ||
880 | data, | ||
881 | size); | ||
882 | } | ||
883 | if (GNUNET_OK != res) | ||
884 | { | ||
885 | GNUNET_break_op (0); | ||
886 | GNUNET_free (data); | ||
887 | return GNUNET_SYSERR; | ||
888 | } | ||
889 | *out = data; | ||
890 | *out_size = size; | ||
891 | return GNUNET_OK; | ||
892 | } | ||
893 | |||
894 | |||
895 | enum GNUNET_GenericReturnValue | ||
896 | GNUNET_STRINGS_parse_uri (const char *path, | ||
897 | char **scheme_part, | ||
898 | const char **path_part) | ||
899 | { | ||
900 | size_t len; | ||
901 | size_t i; | ||
902 | int end; | ||
903 | int pp_state = 0; | ||
904 | const char *post_scheme_part = NULL; | ||
905 | |||
906 | len = strlen (path); | ||
907 | for (end = 0, i = 0; ! end && i < len; i++) | ||
908 | { | ||
909 | switch (pp_state) | ||
910 | { | ||
911 | case 0: | ||
912 | if ((path[i] == ':') && (i > 0)) | ||
913 | { | ||
914 | pp_state += 1; | ||
915 | continue; | ||
916 | } | ||
917 | if (! (((path[i] >= 'A') && (path[i] <= 'Z') ) || | ||
918 | ((path[i] >= 'a') && (path[i] <= 'z') ) || | ||
919 | ((path[i] >= '0') && (path[i] <= '9') ) || (path[i] == '+') || | ||
920 | (path[i] == '-') || (path[i] == '.'))) | ||
921 | end = 1; | ||
922 | break; | ||
923 | |||
924 | case 1: | ||
925 | case 2: | ||
926 | if (path[i] == '/') | ||
927 | { | ||
928 | pp_state += 1; | ||
929 | continue; | ||
930 | } | ||
931 | end = 1; | ||
932 | break; | ||
933 | |||
934 | case 3: | ||
935 | post_scheme_part = &path[i]; | ||
936 | end = 1; | ||
937 | break; | ||
938 | |||
939 | default: | ||
940 | end = 1; | ||
941 | } | ||
942 | } | ||
943 | if (post_scheme_part == NULL) | ||
944 | return GNUNET_NO; | ||
945 | if (scheme_part) | ||
946 | { | ||
947 | *scheme_part = GNUNET_malloc (post_scheme_part - path + 1); | ||
948 | GNUNET_memcpy (*scheme_part, path, post_scheme_part - path); | ||
949 | (*scheme_part)[post_scheme_part - path] = '\0'; | ||
950 | } | ||
951 | if (path_part) | ||
952 | *path_part = post_scheme_part; | ||
953 | return GNUNET_YES; | ||
954 | } | ||
955 | |||
956 | |||
957 | enum GNUNET_GenericReturnValue | ||
958 | GNUNET_STRINGS_path_is_absolute (const char *filename, | ||
959 | int can_be_uri, | ||
960 | int *r_is_uri, | ||
961 | char **r_uri_scheme) | ||
962 | { | ||
963 | const char *post_scheme_path; | ||
964 | int is_uri; | ||
965 | char *uri; | ||
966 | /* consider POSIX paths to be absolute too, even on W32, | ||
967 | * as plibc expansion will fix them for us. | ||
968 | */ | ||
969 | if (filename[0] == '/') | ||
970 | return GNUNET_YES; | ||
971 | if (can_be_uri) | ||
972 | { | ||
973 | is_uri = GNUNET_STRINGS_parse_uri (filename, &uri, &post_scheme_path); | ||
974 | if (r_is_uri) | ||
975 | *r_is_uri = is_uri; | ||
976 | if (is_uri) | ||
977 | { | ||
978 | if (r_uri_scheme) | ||
979 | *r_uri_scheme = uri; | ||
980 | else | ||
981 | GNUNET_free (uri); | ||
982 | |||
983 | return GNUNET_STRINGS_path_is_absolute (post_scheme_path, | ||
984 | GNUNET_NO, | ||
985 | NULL, | ||
986 | NULL); | ||
987 | } | ||
988 | } | ||
989 | else | ||
990 | { | ||
991 | if (r_is_uri) | ||
992 | *r_is_uri = GNUNET_NO; | ||
993 | } | ||
994 | |||
995 | return GNUNET_NO; | ||
996 | } | ||
997 | |||
998 | |||
999 | enum GNUNET_GenericReturnValue | ||
1000 | GNUNET_STRINGS_check_filename (const char *filename, | ||
1001 | enum GNUNET_STRINGS_FilenameCheck checks) | ||
1002 | { | ||
1003 | struct stat st; | ||
1004 | |||
1005 | if ((NULL == filename) || (filename[0] == '\0')) | ||
1006 | return GNUNET_SYSERR; | ||
1007 | if (0 != (checks & GNUNET_STRINGS_CHECK_IS_ABSOLUTE)) | ||
1008 | if (! GNUNET_STRINGS_path_is_absolute (filename, GNUNET_NO, NULL, NULL)) | ||
1009 | return GNUNET_NO; | ||
1010 | if (0 != (checks | ||
1011 | & (GNUNET_STRINGS_CHECK_EXISTS | GNUNET_STRINGS_CHECK_IS_DIRECTORY | ||
1012 | | GNUNET_STRINGS_CHECK_IS_LINK))) | ||
1013 | { | ||
1014 | if (0 != lstat (filename, &st)) | ||
1015 | { | ||
1016 | if (0 != (checks & GNUNET_STRINGS_CHECK_EXISTS)) | ||
1017 | return GNUNET_NO; | ||
1018 | else | ||
1019 | return GNUNET_SYSERR; | ||
1020 | } | ||
1021 | } | ||
1022 | if (0 != (checks & GNUNET_STRINGS_CHECK_IS_DIRECTORY)) | ||
1023 | if (! S_ISDIR (st.st_mode)) | ||
1024 | return GNUNET_NO; | ||
1025 | if (0 != (checks & GNUNET_STRINGS_CHECK_IS_LINK)) | ||
1026 | if (! S_ISLNK (st.st_mode)) | ||
1027 | return GNUNET_NO; | ||
1028 | return GNUNET_YES; | ||
1029 | } | ||
1030 | |||
1031 | |||
1032 | enum GNUNET_GenericReturnValue | ||
1033 | GNUNET_STRINGS_to_address_ipv6 (const char *zt_addr, | ||
1034 | uint16_t addrlen, | ||
1035 | struct sockaddr_in6 *r_buf) | ||
1036 | { | ||
1037 | char zbuf[addrlen + 1]; | ||
1038 | int ret; | ||
1039 | char *port_colon; | ||
1040 | unsigned int port; | ||
1041 | char dummy[2]; | ||
1042 | |||
1043 | if (addrlen < 6) | ||
1044 | return GNUNET_SYSERR; | ||
1045 | GNUNET_memcpy (zbuf, zt_addr, addrlen); | ||
1046 | if ('[' != zbuf[0]) | ||
1047 | { | ||
1048 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1049 | _ ("IPv6 address did not start with `['\n")); | ||
1050 | return GNUNET_SYSERR; | ||
1051 | } | ||
1052 | zbuf[addrlen] = '\0'; | ||
1053 | port_colon = strrchr (zbuf, ':'); | ||
1054 | if (NULL == port_colon) | ||
1055 | { | ||
1056 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1057 | _ ("IPv6 address did contain ':' to separate port number\n")); | ||
1058 | return GNUNET_SYSERR; | ||
1059 | } | ||
1060 | if (']' != *(port_colon - 1)) | ||
1061 | { | ||
1062 | GNUNET_log ( | ||
1063 | GNUNET_ERROR_TYPE_WARNING, | ||
1064 | _ ("IPv6 address did contain ']' before ':' to separate port number\n")); | ||
1065 | return GNUNET_SYSERR; | ||
1066 | } | ||
1067 | ret = sscanf (port_colon, ":%u%1s", &port, dummy); | ||
1068 | if ((1 != ret) || (port > 65535)) | ||
1069 | { | ||
1070 | GNUNET_log ( | ||
1071 | GNUNET_ERROR_TYPE_WARNING, | ||
1072 | _ ("IPv6 address did contain a valid port number after the last ':'\n")); | ||
1073 | return GNUNET_SYSERR; | ||
1074 | } | ||
1075 | *(port_colon - 1) = '\0'; | ||
1076 | memset (r_buf, 0, sizeof(struct sockaddr_in6)); | ||
1077 | ret = inet_pton (AF_INET6, &zbuf[1], &r_buf->sin6_addr); | ||
1078 | if (ret <= 0) | ||
1079 | { | ||
1080 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1081 | _ ("Invalid IPv6 address `%s': %s\n"), | ||
1082 | &zbuf[1], | ||
1083 | strerror (errno)); | ||
1084 | return GNUNET_SYSERR; | ||
1085 | } | ||
1086 | r_buf->sin6_port = htons (port); | ||
1087 | r_buf->sin6_family = AF_INET6; | ||
1088 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
1089 | r_buf->sin6_len = (u_char) sizeof(struct sockaddr_in6); | ||
1090 | #endif | ||
1091 | return GNUNET_OK; | ||
1092 | } | ||
1093 | |||
1094 | |||
1095 | enum GNUNET_GenericReturnValue | ||
1096 | GNUNET_STRINGS_to_address_ipv4 (const char *zt_addr, | ||
1097 | uint16_t addrlen, | ||
1098 | struct sockaddr_in *r_buf) | ||
1099 | { | ||
1100 | unsigned int temps[4]; | ||
1101 | unsigned int port; | ||
1102 | unsigned int cnt; | ||
1103 | char dummy[2]; | ||
1104 | |||
1105 | if (addrlen < 9) | ||
1106 | return GNUNET_SYSERR; | ||
1107 | cnt = sscanf (zt_addr, | ||
1108 | "%u.%u.%u.%u:%u%1s", | ||
1109 | &temps[0], | ||
1110 | &temps[1], | ||
1111 | &temps[2], | ||
1112 | &temps[3], | ||
1113 | &port, | ||
1114 | dummy); | ||
1115 | if (5 != cnt) | ||
1116 | return GNUNET_SYSERR; | ||
1117 | for (cnt = 0; cnt < 4; cnt++) | ||
1118 | if (temps[cnt] > 0xFF) | ||
1119 | return GNUNET_SYSERR; | ||
1120 | if (port > 65535) | ||
1121 | return GNUNET_SYSERR; | ||
1122 | r_buf->sin_family = AF_INET; | ||
1123 | r_buf->sin_port = htons (port); | ||
1124 | r_buf->sin_addr.s_addr = | ||
1125 | htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) + temps[3]); | ||
1126 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
1127 | r_buf->sin_len = (u_char) sizeof(struct sockaddr_in); | ||
1128 | #endif | ||
1129 | return GNUNET_OK; | ||
1130 | } | ||
1131 | |||
1132 | |||
1133 | enum GNUNET_GenericReturnValue | ||
1134 | GNUNET_STRINGS_to_address_ip (const char *addr, | ||
1135 | uint16_t addrlen, | ||
1136 | struct sockaddr_storage *r_buf) | ||
1137 | { | ||
1138 | if (addr[0] == '[') | ||
1139 | return GNUNET_STRINGS_to_address_ipv6 (addr, | ||
1140 | addrlen, | ||
1141 | (struct sockaddr_in6 *) r_buf); | ||
1142 | return GNUNET_STRINGS_to_address_ipv4 (addr, | ||
1143 | addrlen, | ||
1144 | (struct sockaddr_in *) r_buf); | ||
1145 | } | ||
1146 | |||
1147 | |||
1148 | size_t | ||
1149 | GNUNET_STRINGS_parse_socket_addr (const char *addr, | ||
1150 | uint8_t *af, | ||
1151 | struct sockaddr **sa) | ||
1152 | { | ||
1153 | char *cp = GNUNET_strdup (addr); | ||
1154 | |||
1155 | *af = AF_UNSPEC; | ||
1156 | if ('[' == *addr) | ||
1157 | { | ||
1158 | /* IPv6 */ | ||
1159 | *sa = GNUNET_malloc (sizeof(struct sockaddr_in6)); | ||
1160 | if (GNUNET_OK != | ||
1161 | GNUNET_STRINGS_to_address_ipv6 (cp, | ||
1162 | strlen (cp), | ||
1163 | (struct sockaddr_in6 *) *sa)) | ||
1164 | { | ||
1165 | GNUNET_free (*sa); | ||
1166 | *sa = NULL; | ||
1167 | GNUNET_free (cp); | ||
1168 | return 0; | ||
1169 | } | ||
1170 | *af = AF_INET6; | ||
1171 | GNUNET_free (cp); | ||
1172 | return sizeof(struct sockaddr_in6); | ||
1173 | } | ||
1174 | else | ||
1175 | { | ||
1176 | /* IPv4 */ | ||
1177 | *sa = GNUNET_malloc (sizeof(struct sockaddr_in)); | ||
1178 | if (GNUNET_OK != | ||
1179 | GNUNET_STRINGS_to_address_ipv4 (cp, | ||
1180 | strlen (cp), | ||
1181 | (struct sockaddr_in *) *sa)) | ||
1182 | { | ||
1183 | GNUNET_free (*sa); | ||
1184 | *sa = NULL; | ||
1185 | GNUNET_free (cp); | ||
1186 | return 0; | ||
1187 | } | ||
1188 | *af = AF_INET; | ||
1189 | GNUNET_free (cp); | ||
1190 | return sizeof(struct sockaddr_in); | ||
1191 | } | ||
1192 | } | ||
1193 | |||
1194 | |||
1195 | /** | ||
1196 | * Makes a copy of argv that consists of a single memory chunk that can be | ||
1197 | * freed with a single call to GNUNET_free(); | ||
1198 | */ | ||
1199 | static char *const * | ||
1200 | _make_continuous_arg_copy (int argc, char *const *argv) | ||
1201 | { | ||
1202 | size_t argvsize = 0; | ||
1203 | char **new_argv; | ||
1204 | char *p; | ||
1205 | |||
1206 | for (int i = 0; i < argc; i++) | ||
1207 | argvsize += strlen (argv[i]) + 1 + sizeof(char *); | ||
1208 | new_argv = GNUNET_malloc (argvsize + sizeof(char *)); | ||
1209 | p = (char *) &new_argv[argc + 1]; | ||
1210 | for (int i = 0; i < argc; i++) | ||
1211 | { | ||
1212 | new_argv[i] = p; | ||
1213 | strcpy (p, argv[i]); | ||
1214 | p += strlen (argv[i]) + 1; | ||
1215 | } | ||
1216 | new_argv[argc] = NULL; | ||
1217 | return (char *const *) new_argv; | ||
1218 | } | ||
1219 | |||
1220 | |||
1221 | enum GNUNET_GenericReturnValue | ||
1222 | GNUNET_STRINGS_get_utf8_args (int argc, | ||
1223 | char *const *argv, | ||
1224 | int *u8argc, | ||
1225 | char *const **u8argv) | ||
1226 | { | ||
1227 | char *const *new_argv = | ||
1228 | (char *const *) _make_continuous_arg_copy (argc, argv); | ||
1229 | *u8argv = new_argv; | ||
1230 | *u8argc = argc; | ||
1231 | return GNUNET_OK; | ||
1232 | } | ||
1233 | |||
1234 | |||
1235 | /** | ||
1236 | * Parse the given port policy. The format is | ||
1237 | * "[!]SPORT[-DPORT]". | ||
1238 | * | ||
1239 | * @param port_policy string to parse | ||
1240 | * @param pp policy to fill in | ||
1241 | * @return #GNUNET_OK on success, #GNUNET_SYSERR if the | ||
1242 | * @a port_policy is malformed | ||
1243 | */ | ||
1244 | static enum GNUNET_GenericReturnValue | ||
1245 | parse_port_policy (const char *port_policy, | ||
1246 | struct GNUNET_STRINGS_PortPolicy *pp) | ||
1247 | { | ||
1248 | const char *pos; | ||
1249 | int s; | ||
1250 | int e; | ||
1251 | char eol[2]; | ||
1252 | |||
1253 | pos = port_policy; | ||
1254 | if ('!' == *pos) | ||
1255 | { | ||
1256 | pp->negate_portrange = GNUNET_YES; | ||
1257 | pos++; | ||
1258 | } | ||
1259 | if (2 == sscanf (pos, "%u-%u%1s", &s, &e, eol)) | ||
1260 | { | ||
1261 | if ((0 == s) || (s > 0xFFFF) || (e < s) || (e > 0xFFFF)) | ||
1262 | { | ||
1263 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Port not in range\n")); | ||
1264 | return GNUNET_SYSERR; | ||
1265 | } | ||
1266 | pp->start_port = (uint16_t) s; | ||
1267 | pp->end_port = (uint16_t) e; | ||
1268 | return GNUNET_OK; | ||
1269 | } | ||
1270 | if (1 == sscanf (pos, "%u%1s", &s, eol)) | ||
1271 | { | ||
1272 | if ((0 == s) || (s > 0xFFFF)) | ||
1273 | { | ||
1274 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Port not in range\n")); | ||
1275 | return GNUNET_SYSERR; | ||
1276 | } | ||
1277 | |||
1278 | pp->start_port = (uint16_t) s; | ||
1279 | pp->end_port = (uint16_t) s; | ||
1280 | return GNUNET_OK; | ||
1281 | } | ||
1282 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1283 | _ ("Malformed port policy `%s'\n"), | ||
1284 | port_policy); | ||
1285 | return GNUNET_SYSERR; | ||
1286 | } | ||
1287 | |||
1288 | |||
1289 | struct GNUNET_STRINGS_IPv4NetworkPolicy * | ||
1290 | GNUNET_STRINGS_parse_ipv4_policy (const char *routeListX) | ||
1291 | { | ||
1292 | unsigned int count; | ||
1293 | unsigned int i; | ||
1294 | unsigned int j; | ||
1295 | unsigned int len; | ||
1296 | int cnt; | ||
1297 | unsigned int pos; | ||
1298 | unsigned int temps[8]; | ||
1299 | int slash; | ||
1300 | struct GNUNET_STRINGS_IPv4NetworkPolicy *result; | ||
1301 | int colon; | ||
1302 | int end; | ||
1303 | char *routeList; | ||
1304 | char dummy[2]; | ||
1305 | |||
1306 | if (NULL == routeListX) | ||
1307 | return NULL; | ||
1308 | len = strlen (routeListX); | ||
1309 | if (0 == len) | ||
1310 | return NULL; | ||
1311 | routeList = GNUNET_strdup (routeListX); | ||
1312 | count = 0; | ||
1313 | for (i = 0; i < len; i++) | ||
1314 | if (routeList[i] == ';') | ||
1315 | count++; | ||
1316 | result = GNUNET_malloc (sizeof(struct GNUNET_STRINGS_IPv4NetworkPolicy) | ||
1317 | * (count + 1)); | ||
1318 | i = 0; | ||
1319 | pos = 0; | ||
1320 | while (i < count) | ||
1321 | { | ||
1322 | for (colon = pos; ':' != routeList[colon]; colon++) | ||
1323 | if ((';' == routeList[colon]) || ('\0' == routeList[colon])) | ||
1324 | break; | ||
1325 | for (end = colon; ';' != routeList[end]; end++) | ||
1326 | if ('\0' == routeList[end]) | ||
1327 | break; | ||
1328 | if ('\0' == routeList[end]) | ||
1329 | break; | ||
1330 | routeList[end] = '\0'; | ||
1331 | if (':' == routeList[colon]) | ||
1332 | { | ||
1333 | routeList[colon] = '\0'; | ||
1334 | if (GNUNET_OK != parse_port_policy (&routeList[colon + 1], &result[i].pp)) | ||
1335 | break; | ||
1336 | } | ||
1337 | cnt = sscanf (&routeList[pos], | ||
1338 | "%u.%u.%u.%u/%u.%u.%u.%u%1s", | ||
1339 | &temps[0], | ||
1340 | &temps[1], | ||
1341 | &temps[2], | ||
1342 | &temps[3], | ||
1343 | &temps[4], | ||
1344 | &temps[5], | ||
1345 | &temps[6], | ||
1346 | &temps[7], | ||
1347 | dummy); | ||
1348 | if (8 == cnt) | ||
1349 | { | ||
1350 | for (j = 0; j < 8; j++) | ||
1351 | if (temps[j] > 0xFF) | ||
1352 | { | ||
1353 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1354 | _ ("Invalid format for IP: `%s'\n"), | ||
1355 | &routeList[pos]); | ||
1356 | GNUNET_free (result); | ||
1357 | GNUNET_free (routeList); | ||
1358 | return NULL; | ||
1359 | } | ||
1360 | result[i].network.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16) | ||
1361 | + (temps[2] << 8) + temps[3]); | ||
1362 | result[i].netmask.s_addr = htonl ((temps[4] << 24) + (temps[5] << 16) | ||
1363 | + (temps[6] << 8) + temps[7]); | ||
1364 | pos = end + 1; | ||
1365 | i++; | ||
1366 | continue; | ||
1367 | } | ||
1368 | /* try second notation */ | ||
1369 | cnt = sscanf (&routeList[pos], | ||
1370 | "%u.%u.%u.%u/%u%1s", | ||
1371 | &temps[0], | ||
1372 | &temps[1], | ||
1373 | &temps[2], | ||
1374 | &temps[3], | ||
1375 | &slash, | ||
1376 | dummy); | ||
1377 | if (5 == cnt) | ||
1378 | { | ||
1379 | for (j = 0; j < 4; j++) | ||
1380 | if (temps[j] > 0xFF) | ||
1381 | { | ||
1382 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1383 | _ ("Invalid format for IP: `%s'\n"), | ||
1384 | &routeList[pos]); | ||
1385 | GNUNET_free (result); | ||
1386 | GNUNET_free (routeList); | ||
1387 | return NULL; | ||
1388 | } | ||
1389 | result[i].network.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16) | ||
1390 | + (temps[2] << 8) + temps[3]); | ||
1391 | if ((slash <= 32) && (slash >= 0)) | ||
1392 | { | ||
1393 | result[i].netmask.s_addr = 0; | ||
1394 | while (slash > 0) | ||
1395 | { | ||
1396 | result[i].netmask.s_addr = | ||
1397 | (result[i].netmask.s_addr >> 1) + 0x80000000; | ||
1398 | slash--; | ||
1399 | } | ||
1400 | result[i].netmask.s_addr = htonl (result[i].netmask.s_addr); | ||
1401 | pos = end + 1; | ||
1402 | i++; | ||
1403 | continue; | ||
1404 | } | ||
1405 | else | ||
1406 | { | ||
1407 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1408 | _ ("Invalid network notation ('/%d' is not legal in IPv4 CIDR)."), | ||
1409 | slash); | ||
1410 | GNUNET_free (result); | ||
1411 | GNUNET_free (routeList); | ||
1412 | return NULL; /* error */ | ||
1413 | } | ||
1414 | } | ||
1415 | /* try third notation */ | ||
1416 | slash = 32; | ||
1417 | cnt = sscanf (&routeList[pos], | ||
1418 | "%u.%u.%u.%u%1s", | ||
1419 | &temps[0], | ||
1420 | &temps[1], | ||
1421 | &temps[2], | ||
1422 | &temps[3], | ||
1423 | dummy); | ||
1424 | if (4 == cnt) | ||
1425 | { | ||
1426 | for (j = 0; j < 4; j++) | ||
1427 | if (temps[j] > 0xFF) | ||
1428 | { | ||
1429 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1430 | _ ("Invalid format for IP: `%s'\n"), | ||
1431 | &routeList[pos]); | ||
1432 | GNUNET_free (result); | ||
1433 | GNUNET_free (routeList); | ||
1434 | return NULL; | ||
1435 | } | ||
1436 | result[i].network.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16) | ||
1437 | + (temps[2] << 8) + temps[3]); | ||
1438 | result[i].netmask.s_addr = 0; | ||
1439 | while (slash > 0) | ||
1440 | { | ||
1441 | result[i].netmask.s_addr = (result[i].netmask.s_addr >> 1) + 0x80000000; | ||
1442 | slash--; | ||
1443 | } | ||
1444 | result[i].netmask.s_addr = htonl (result[i].netmask.s_addr); | ||
1445 | pos = end + 1; | ||
1446 | i++; | ||
1447 | continue; | ||
1448 | } | ||
1449 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1450 | _ ("Invalid format for IP: `%s'\n"), | ||
1451 | &routeList[pos]); | ||
1452 | GNUNET_free (result); | ||
1453 | GNUNET_free (routeList); | ||
1454 | return NULL; /* error */ | ||
1455 | } | ||
1456 | if (pos < strlen (routeList)) | ||
1457 | { | ||
1458 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1459 | _ ("Invalid format: `%s'\n"), | ||
1460 | &routeListX[pos]); | ||
1461 | GNUNET_free (result); | ||
1462 | GNUNET_free (routeList); | ||
1463 | return NULL; /* oops */ | ||
1464 | } | ||
1465 | GNUNET_free (routeList); | ||
1466 | return result; /* ok */ | ||
1467 | } | ||
1468 | |||
1469 | |||
1470 | struct GNUNET_STRINGS_IPv6NetworkPolicy * | ||
1471 | GNUNET_STRINGS_parse_ipv6_policy (const char *routeListX) | ||
1472 | { | ||
1473 | unsigned int count; | ||
1474 | unsigned int i; | ||
1475 | unsigned int len; | ||
1476 | unsigned int pos; | ||
1477 | int start; | ||
1478 | int slash; | ||
1479 | int ret; | ||
1480 | char *routeList; | ||
1481 | struct GNUNET_STRINGS_IPv6NetworkPolicy *result; | ||
1482 | unsigned int bits; | ||
1483 | unsigned int off; | ||
1484 | int save; | ||
1485 | int colon; | ||
1486 | char dummy[2]; | ||
1487 | |||
1488 | if (NULL == routeListX) | ||
1489 | return NULL; | ||
1490 | len = strlen (routeListX); | ||
1491 | if (0 == len) | ||
1492 | return NULL; | ||
1493 | routeList = GNUNET_strdup (routeListX); | ||
1494 | count = 0; | ||
1495 | for (i = 0; i < len; i++) | ||
1496 | if (';' == routeList[i]) | ||
1497 | count++; | ||
1498 | if (';' != routeList[len - 1]) | ||
1499 | { | ||
1500 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1501 | _ ("Invalid network notation (does not end with ';': `%s')\n"), | ||
1502 | routeList); | ||
1503 | GNUNET_free (routeList); | ||
1504 | return NULL; | ||
1505 | } | ||
1506 | |||
1507 | result = GNUNET_malloc (sizeof(struct GNUNET_STRINGS_IPv6NetworkPolicy) | ||
1508 | * (count + 1)); | ||
1509 | i = 0; | ||
1510 | pos = 0; | ||
1511 | while (i < count) | ||
1512 | { | ||
1513 | start = pos; | ||
1514 | while (';' != routeList[pos]) | ||
1515 | pos++; | ||
1516 | slash = pos; | ||
1517 | while ((slash >= start) && (routeList[slash] != '/')) | ||
1518 | slash--; | ||
1519 | |||
1520 | if (slash < start) | ||
1521 | { | ||
1522 | memset (&result[i].netmask, 0xFF, sizeof(struct in6_addr)); | ||
1523 | slash = pos; | ||
1524 | } | ||
1525 | else | ||
1526 | { | ||
1527 | routeList[pos] = '\0'; | ||
1528 | for (colon = pos; ':' != routeList[colon]; colon--) | ||
1529 | if ('/' == routeList[colon]) | ||
1530 | break; | ||
1531 | if (':' == routeList[colon]) | ||
1532 | { | ||
1533 | routeList[colon] = '\0'; | ||
1534 | if (GNUNET_OK != | ||
1535 | parse_port_policy (&routeList[colon + 1], &result[i].pp)) | ||
1536 | { | ||
1537 | GNUNET_free (result); | ||
1538 | GNUNET_free (routeList); | ||
1539 | return NULL; | ||
1540 | } | ||
1541 | } | ||
1542 | ret = inet_pton (AF_INET6, &routeList[slash + 1], &result[i].netmask); | ||
1543 | if (ret <= 0) | ||
1544 | { | ||
1545 | save = errno; | ||
1546 | if ((1 != sscanf (&routeList[slash + 1], "%u%1s", &bits, dummy)) || | ||
1547 | (bits > 128)) | ||
1548 | { | ||
1549 | if (0 == ret) | ||
1550 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1551 | _ ("Wrong format `%s' for netmask\n"), | ||
1552 | &routeList[slash + 1]); | ||
1553 | else | ||
1554 | { | ||
1555 | errno = save; | ||
1556 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_pton"); | ||
1557 | } | ||
1558 | GNUNET_free (result); | ||
1559 | GNUNET_free (routeList); | ||
1560 | return NULL; | ||
1561 | } | ||
1562 | off = 0; | ||
1563 | while (bits > 8) | ||
1564 | { | ||
1565 | result[i].netmask.s6_addr[off++] = 0xFF; | ||
1566 | bits -= 8; | ||
1567 | } | ||
1568 | while (bits > 0) | ||
1569 | { | ||
1570 | result[i].netmask.s6_addr[off] = | ||
1571 | (result[i].netmask.s6_addr[off] >> 1) + 0x80; | ||
1572 | bits--; | ||
1573 | } | ||
1574 | } | ||
1575 | } | ||
1576 | routeList[slash] = '\0'; | ||
1577 | ret = inet_pton (AF_INET6, &routeList[start], &result[i].network); | ||
1578 | if (ret <= 0) | ||
1579 | { | ||
1580 | if (0 == ret) | ||
1581 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1582 | _ ("Wrong format `%s' for network\n"), | ||
1583 | &routeList[slash + 1]); | ||
1584 | else | ||
1585 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "inet_pton"); | ||
1586 | GNUNET_free (result); | ||
1587 | GNUNET_free (routeList); | ||
1588 | return NULL; | ||
1589 | } | ||
1590 | pos++; | ||
1591 | i++; | ||
1592 | } | ||
1593 | GNUNET_free (routeList); | ||
1594 | return result; | ||
1595 | } | ||
1596 | |||
1597 | |||
1598 | /** ******************** Base64 encoding ***********/ | ||
1599 | |||
1600 | #define FILLCHAR '=' | ||
1601 | static char *cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
1602 | "abcdefghijklmnopqrstuvwxyz" | ||
1603 | "0123456789+/"; | ||
1604 | |||
1605 | |||
1606 | size_t | ||
1607 | GNUNET_STRINGS_base64_encode (const void *in, | ||
1608 | size_t len, | ||
1609 | char **output) | ||
1610 | { | ||
1611 | const char *data = in; | ||
1612 | size_t ret; | ||
1613 | char *opt; | ||
1614 | |||
1615 | ret = 0; | ||
1616 | GNUNET_assert (len < SIZE_MAX / 4 * 3); | ||
1617 | opt = GNUNET_malloc (2 + (len * 4 / 3) + 8); | ||
1618 | for (size_t i = 0; i < len; ++i) | ||
1619 | { | ||
1620 | char c; | ||
1621 | |||
1622 | c = (data[i] >> 2) & 0x3f; | ||
1623 | opt[ret++] = cvt[(int) c]; | ||
1624 | c = (data[i] << 4) & 0x3f; | ||
1625 | if (++i < len) | ||
1626 | c |= (data[i] >> 4) & 0x0f; | ||
1627 | opt[ret++] = cvt[(int) c]; | ||
1628 | if (i < len) | ||
1629 | { | ||
1630 | c = (data[i] << 2) & 0x3f; | ||
1631 | if (++i < len) | ||
1632 | c |= (data[i] >> 6) & 0x03; | ||
1633 | opt[ret++] = cvt[(int) c]; | ||
1634 | } | ||
1635 | else | ||
1636 | { | ||
1637 | ++i; | ||
1638 | opt[ret++] = FILLCHAR; | ||
1639 | } | ||
1640 | if (i < len) | ||
1641 | { | ||
1642 | c = data[i] & 0x3f; | ||
1643 | opt[ret++] = cvt[(int) c]; | ||
1644 | } | ||
1645 | else | ||
1646 | { | ||
1647 | opt[ret++] = FILLCHAR; | ||
1648 | } | ||
1649 | } | ||
1650 | *output = opt; | ||
1651 | return ret; | ||
1652 | } | ||
1653 | |||
1654 | |||
1655 | size_t | ||
1656 | GNUNET_STRINGS_base64url_encode (const void *in, | ||
1657 | size_t len, | ||
1658 | char **output) | ||
1659 | { | ||
1660 | char *enc; | ||
1661 | size_t pos; | ||
1662 | |||
1663 | GNUNET_STRINGS_base64_encode (in, len, output); | ||
1664 | enc = *output; | ||
1665 | /* Replace with correct characters for base64url */ | ||
1666 | pos = 0; | ||
1667 | while ('\0' != enc[pos]) | ||
1668 | { | ||
1669 | if ('+' == enc[pos]) | ||
1670 | enc[pos] = '-'; | ||
1671 | if ('/' == enc[pos]) | ||
1672 | enc[pos] = '_'; | ||
1673 | if ('=' == enc[pos]) | ||
1674 | { | ||
1675 | enc[pos] = '\0'; | ||
1676 | break; | ||
1677 | } | ||
1678 | pos++; | ||
1679 | } | ||
1680 | return strlen (enc); | ||
1681 | } | ||
1682 | |||
1683 | |||
1684 | #define cvtfind(a) \ | ||
1685 | ((((a) >= 'A') && ((a) <= 'Z')) \ | ||
1686 | ? (a) - 'A' \ | ||
1687 | : (((a) >= 'a') && ((a) <= 'z')) \ | ||
1688 | ? (a) - 'a' + 26 \ | ||
1689 | : (((a) >= '0') && ((a) <= '9')) \ | ||
1690 | ? (a) - '0' + 52 \ | ||
1691 | : ((a) == '+') ? 62 : ((a) == '/') ? 63 : -1) | ||
1692 | |||
1693 | |||
1694 | size_t | ||
1695 | GNUNET_STRINGS_base64_decode (const char *data, | ||
1696 | size_t len, | ||
1697 | void **out) | ||
1698 | { | ||
1699 | char *output; | ||
1700 | size_t ret = 0; | ||
1701 | |||
1702 | #define CHECK_CRLF \ | ||
1703 | while (data[i] == '\r' || data[i] == '\n') \ | ||
1704 | { \ | ||
1705 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, \ | ||
1706 | "ignoring CR/LF\n"); \ | ||
1707 | i++; \ | ||
1708 | if (i >= len) \ | ||
1709 | goto END; \ | ||
1710 | } | ||
1711 | |||
1712 | GNUNET_assert (len / 3 < SIZE_MAX); | ||
1713 | output = GNUNET_malloc ((len * 3 / 4) + 8); | ||
1714 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1715 | "base64_decode decoding len=%d\n", | ||
1716 | (int) len); | ||
1717 | for (size_t i = 0; i < len; ++i) | ||
1718 | { | ||
1719 | char c; | ||
1720 | char c1; | ||
1721 | |||
1722 | CHECK_CRLF; | ||
1723 | if (FILLCHAR == data[i]) | ||
1724 | break; | ||
1725 | c = (char) cvtfind (data[i]); | ||
1726 | ++i; | ||
1727 | CHECK_CRLF; | ||
1728 | c1 = (char) cvtfind (data[i]); | ||
1729 | c = (c << 2) | ((c1 >> 4) & 0x3); | ||
1730 | output[ret++] = c; | ||
1731 | if (++i < len) | ||
1732 | { | ||
1733 | CHECK_CRLF; | ||
1734 | c = data[i]; | ||
1735 | if (FILLCHAR == c) | ||
1736 | break; | ||
1737 | c = (char) cvtfind (c); | ||
1738 | c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf); | ||
1739 | output[ret++] = c1; | ||
1740 | } | ||
1741 | if (++i < len) | ||
1742 | { | ||
1743 | CHECK_CRLF; | ||
1744 | c1 = data[i]; | ||
1745 | if (FILLCHAR == c1) | ||
1746 | break; | ||
1747 | |||
1748 | c1 = (char) cvtfind (c1); | ||
1749 | c = ((c << 6) & 0xc0) | c1; | ||
1750 | output[ret++] = c; | ||
1751 | } | ||
1752 | } | ||
1753 | END: | ||
1754 | *out = output; | ||
1755 | return ret; | ||
1756 | } | ||
1757 | |||
1758 | |||
1759 | size_t | ||
1760 | GNUNET_STRINGS_base64url_decode (const char *data, | ||
1761 | size_t len, | ||
1762 | void **out) | ||
1763 | { | ||
1764 | char *s; | ||
1765 | int padding; | ||
1766 | size_t ret; | ||
1767 | |||
1768 | /* make enough space for padding */ | ||
1769 | GNUNET_assert (len < SIZE_MAX - 3); | ||
1770 | s = GNUNET_malloc (len + 3); | ||
1771 | memcpy (s, data, len); | ||
1772 | |||
1773 | for (int i = 0; i < strlen (s); i++) | ||
1774 | { | ||
1775 | if (s[i] == '-') | ||
1776 | s[i] = '+'; | ||
1777 | if (s[i] == '_') | ||
1778 | s[i] = '/'; | ||
1779 | } | ||
1780 | padding = len % 4; | ||
1781 | switch (padding) // Pad with trailing '='s | ||
1782 | { | ||
1783 | case 0: | ||
1784 | break; // No pad chars in this case | ||
1785 | case 2: | ||
1786 | memcpy (&s[len], | ||
1787 | "==", | ||
1788 | 2); | ||
1789 | len += 2; | ||
1790 | break; // Two pad chars | ||
1791 | case 3: | ||
1792 | s[len] = '='; | ||
1793 | len++; | ||
1794 | break; // One pad char | ||
1795 | default: | ||
1796 | GNUNET_assert (0); | ||
1797 | break; | ||
1798 | } | ||
1799 | ret = GNUNET_STRINGS_base64_decode (s, len, out); | ||
1800 | GNUNET_free (s); | ||
1801 | return ret; | ||
1802 | } | ||
1803 | |||
1804 | |||
1805 | size_t | ||
1806 | GNUNET_STRINGS_urldecode (const char *data, | ||
1807 | size_t len, | ||
1808 | char **out) | ||
1809 | { | ||
1810 | const char *rpos = data; | ||
1811 | *out = GNUNET_malloc (len + 1); /* output should always fit into input */ | ||
1812 | char *wpos = *out; | ||
1813 | size_t resl = 0; | ||
1814 | |||
1815 | while ( ('\0' != *rpos) && | ||
1816 | (data + len != rpos) ) | ||
1817 | { | ||
1818 | unsigned int num; | ||
1819 | switch (*rpos) | ||
1820 | { | ||
1821 | case '%': | ||
1822 | if (rpos + 3 > data + len) | ||
1823 | { | ||
1824 | GNUNET_break_op (0); | ||
1825 | GNUNET_free (*out); | ||
1826 | return 0; | ||
1827 | } | ||
1828 | if (1 != sscanf (rpos + 1, "%2x", &num)) | ||
1829 | break; | ||
1830 | *wpos = (char) ((unsigned char) num); | ||
1831 | wpos++; | ||
1832 | resl++; | ||
1833 | rpos += 3; | ||
1834 | break; | ||
1835 | /* TODO: add bad sequence handling */ | ||
1836 | /* intentional fall through! */ | ||
1837 | default: | ||
1838 | *wpos = *rpos; | ||
1839 | wpos++; | ||
1840 | resl++; | ||
1841 | rpos++; | ||
1842 | } | ||
1843 | } | ||
1844 | *wpos = '\0'; /* add 0-terminator */ | ||
1845 | return resl; | ||
1846 | } | ||
1847 | |||
1848 | |||
1849 | size_t | ||
1850 | GNUNET_STRINGS_urlencode (const char *data, | ||
1851 | size_t len, | ||
1852 | char **out) | ||
1853 | { | ||
1854 | struct GNUNET_Buffer buf = { 0 }; | ||
1855 | const uint8_t *i8 = (uint8_t *) data; | ||
1856 | |||
1857 | while (0 != *i8) | ||
1858 | { | ||
1859 | if (0 == (0x80 & *i8)) | ||
1860 | { | ||
1861 | /* traditional ASCII */ | ||
1862 | if ( isalnum (*i8) || | ||
1863 | (*i8 == '-') || | ||
1864 | (*i8 == '_') || | ||
1865 | (*i8 == '.') || | ||
1866 | (*i8 == '~') ) | ||
1867 | GNUNET_buffer_write (&buf, | ||
1868 | (const char*) i8, | ||
1869 | 1); | ||
1870 | else if (*i8 == ' ') | ||
1871 | GNUNET_buffer_write (&buf, | ||
1872 | "+", | ||
1873 | 1); | ||
1874 | else | ||
1875 | GNUNET_buffer_write_fstr (&buf, | ||
1876 | "%%%X%X", | ||
1877 | *i8 >> 4, | ||
1878 | *i8 & 15); | ||
1879 | i8++; | ||
1880 | continue; | ||
1881 | } | ||
1882 | if (0x80 + 0x40 == ((0x80 + 0x40 + 0x20) & *i8)) | ||
1883 | { | ||
1884 | /* 2-byte value, percent-encode */ | ||
1885 | GNUNET_buffer_write_fstr (&buf, | ||
1886 | "%%%X%X", | ||
1887 | *i8 >> 4, | ||
1888 | *i8 & 15); | ||
1889 | i8++; | ||
1890 | GNUNET_buffer_write_fstr (&buf, | ||
1891 | "%%%X%X", | ||
1892 | *i8 >> 4, | ||
1893 | *i8 & 15); | ||
1894 | i8++; | ||
1895 | continue; | ||
1896 | } | ||
1897 | if (0x80 + 0x40 + 0x20 == ((0x80 + 0x40 + 0x20 + 0x10) & *i8)) | ||
1898 | { | ||
1899 | /* 3-byte value, percent-encode */ | ||
1900 | for (unsigned int i = 0; i<3; i++) | ||
1901 | { | ||
1902 | GNUNET_buffer_write_fstr (&buf, | ||
1903 | "%%%X%X", | ||
1904 | *i8 >> 4, | ||
1905 | *i8 & 15); | ||
1906 | i8++; | ||
1907 | } | ||
1908 | continue; | ||
1909 | } | ||
1910 | if (0x80 + 0x40 + 0x20 + 0x10 == ((0x80 + 0x40 + 0x20 + 0x10 + 0x08) & *i8)) | ||
1911 | { | ||
1912 | /* 4-byte value, percent-encode */ | ||
1913 | for (unsigned int i = 0; i<4; i++) | ||
1914 | { | ||
1915 | GNUNET_buffer_write_fstr (&buf, | ||
1916 | "%%%X%X", | ||
1917 | *i8 >> 4, | ||
1918 | *i8 & 15); | ||
1919 | i8++; | ||
1920 | } | ||
1921 | continue; | ||
1922 | } | ||
1923 | if (0x80 + 0x40 + 0x20 + 0x10 + 0x08 == ((0x80 + 0x40 + 0x20 + 0x10 + 0x08 | ||
1924 | + 0x04) & *i8)) | ||
1925 | { | ||
1926 | /* 5-byte value, percent-encode (outside of UTF-8 modern standard, but so what) */ | ||
1927 | for (unsigned int i = 0; i<5; i++) | ||
1928 | { | ||
1929 | GNUNET_buffer_write_fstr (&buf, | ||
1930 | "%%%X%X", | ||
1931 | *i8 >> 4, | ||
1932 | *i8 & 15); | ||
1933 | i8++; | ||
1934 | } | ||
1935 | continue; | ||
1936 | } | ||
1937 | if (0x80 + 0x40 + 0x20 + 0x10 + 0x08 + 0x04 == ((0x80 + 0x40 + 0x20 + 0x10 | ||
1938 | + 0x08 + 0x04 + 0x02) | ||
1939 | & *i8)) | ||
1940 | { | ||
1941 | /* 6-byte value, percent-encode (outside of UTF-8 modern standard, but so what) */ | ||
1942 | for (unsigned int i = 0; i<6; i++) | ||
1943 | { | ||
1944 | GNUNET_buffer_write_fstr (&buf, | ||
1945 | "%%%X%X", | ||
1946 | *i8 >> 4, | ||
1947 | *i8 & 15); | ||
1948 | i8++; | ||
1949 | } | ||
1950 | continue; | ||
1951 | } | ||
1952 | /* really, really invalid UTF-8: fail */ | ||
1953 | GNUNET_break (0); | ||
1954 | GNUNET_buffer_clear (&buf); | ||
1955 | return 0; | ||
1956 | } | ||
1957 | *out = GNUNET_buffer_reap_str (&buf); | ||
1958 | return strlen (*out); | ||
1959 | } | ||
1960 | |||
1961 | |||
1962 | /** | ||
1963 | * Sometimes we use the binary name to determine which specific | ||
1964 | * test to run. In those cases, the string after the last "_" | ||
1965 | * in 'argv[0]' specifies a string that determines the configuration | ||
1966 | * file or plugin to use. | ||
1967 | * | ||
1968 | * This function returns the respective substring, taking care | ||
1969 | * of issues such as binaries ending in '.exe' on W32. | ||
1970 | * | ||
1971 | * @param argv0 the name of the binary | ||
1972 | * @return string between the last '_' and the '.exe' (or the end of the string), | ||
1973 | * NULL if argv0 has no '_' | ||
1974 | */ | ||
1975 | char * | ||
1976 | GNUNET_STRINGS_get_suffix_from_binary_name (const char *argv0) | ||
1977 | { | ||
1978 | const char *ret; | ||
1979 | const char *dot; | ||
1980 | |||
1981 | ret = strrchr (argv0, '_'); | ||
1982 | if (NULL == ret) | ||
1983 | return NULL; | ||
1984 | ret++; /* skip underscore */ | ||
1985 | dot = strchr (ret, | ||
1986 | '.'); | ||
1987 | if (NULL != dot) | ||
1988 | return GNUNET_strndup (ret, | ||
1989 | dot - ret); | ||
1990 | return GNUNET_strdup (ret); | ||
1991 | } | ||
1992 | |||
1993 | |||
1994 | /* end of strings.c */ | ||