diff options
Diffstat (limited to 'src/lib/util/strings.c')
-rw-r--r-- | src/lib/util/strings.c | 2007 |
1 files changed, 2007 insertions, 0 deletions
diff --git a/src/lib/util/strings.c b/src/lib/util/strings.c new file mode 100644 index 000000000..e0ae6a2fa --- /dev/null +++ b/src/lib/util/strings.c | |||
@@ -0,0 +1,2007 @@ | |||
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 | |||
449 | enum GNUNET_GenericReturnValue | ||
450 | GNUNET_STRINGS_utf8_tolower (const char *input, | ||
451 | char *output) | ||
452 | { | ||
453 | uint8_t *tmp_in; | ||
454 | size_t len; | ||
455 | |||
456 | tmp_in = u8_tolower ((uint8_t *) input, | ||
457 | strlen ((char *) input), | ||
458 | NULL, | ||
459 | UNINORM_NFD, | ||
460 | NULL, | ||
461 | &len); | ||
462 | if (NULL == tmp_in) | ||
463 | return GNUNET_SYSERR; | ||
464 | GNUNET_memcpy (output, tmp_in, len); | ||
465 | output[len] = '\0'; | ||
466 | GNUNET_free (tmp_in); | ||
467 | return GNUNET_OK; | ||
468 | } | ||
469 | |||
470 | |||
471 | enum GNUNET_GenericReturnValue | ||
472 | GNUNET_STRINGS_utf8_toupper (const char *input, | ||
473 | char *output) | ||
474 | { | ||
475 | uint8_t *tmp_in; | ||
476 | size_t len; | ||
477 | |||
478 | tmp_in = u8_toupper ((uint8_t *) input, | ||
479 | strlen ((char *) input), | ||
480 | NULL, | ||
481 | UNINORM_NFD, | ||
482 | NULL, | ||
483 | &len); | ||
484 | if (NULL == tmp_in) | ||
485 | return GNUNET_SYSERR; | ||
486 | /* 0-terminator does not fit */ | ||
487 | GNUNET_memcpy (output, tmp_in, len); | ||
488 | output[len] = '\0'; | ||
489 | GNUNET_free (tmp_in); | ||
490 | return GNUNET_OK; | ||
491 | } | ||
492 | |||
493 | |||
494 | char * | ||
495 | GNUNET_STRINGS_filename_expand (const char *fil) | ||
496 | { | ||
497 | char *buffer; | ||
498 | size_t len; | ||
499 | char *fm; | ||
500 | const char *fil_ptr; | ||
501 | |||
502 | if (fil == NULL) | ||
503 | return NULL; | ||
504 | |||
505 | if (fil[0] == DIR_SEPARATOR) | ||
506 | /* absolute path, just copy */ | ||
507 | return GNUNET_strdup (fil); | ||
508 | if (fil[0] == '~') | ||
509 | { | ||
510 | fm = getenv ("HOME"); | ||
511 | if (fm == NULL) | ||
512 | { | ||
513 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
514 | _ ("Failed to expand `$HOME': environment variable `HOME' not set")); | ||
515 | return NULL; | ||
516 | } | ||
517 | fm = GNUNET_strdup (fm); | ||
518 | /* do not copy '~' */ | ||
519 | fil_ptr = fil + 1; | ||
520 | |||
521 | /* skip over dir separator to be consistent */ | ||
522 | if (fil_ptr[0] == DIR_SEPARATOR) | ||
523 | fil_ptr++; | ||
524 | } | ||
525 | else | ||
526 | { | ||
527 | /* relative path */ | ||
528 | fil_ptr = fil; | ||
529 | len = 512; | ||
530 | fm = NULL; | ||
531 | while (1) | ||
532 | { | ||
533 | buffer = GNUNET_malloc (len); | ||
534 | if (getcwd (buffer, len) != NULL) | ||
535 | { | ||
536 | fm = buffer; | ||
537 | break; | ||
538 | } | ||
539 | if ((errno == ERANGE) && (len < 1024 * 1024 * 4)) | ||
540 | { | ||
541 | len *= 2; | ||
542 | GNUNET_free (buffer); | ||
543 | continue; | ||
544 | } | ||
545 | GNUNET_free (buffer); | ||
546 | break; | ||
547 | } | ||
548 | if (fm == NULL) | ||
549 | { | ||
550 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "getcwd"); | ||
551 | buffer = getenv ("PWD"); /* alternative */ | ||
552 | if (buffer != NULL) | ||
553 | fm = GNUNET_strdup (buffer); | ||
554 | } | ||
555 | if (fm == NULL) | ||
556 | fm = GNUNET_strdup ("./"); /* give up */ | ||
557 | } | ||
558 | GNUNET_asprintf (&buffer, | ||
559 | "%s%s%s", | ||
560 | fm, | ||
561 | (fm[strlen (fm) - 1] == DIR_SEPARATOR) ? "" | ||
562 | : DIR_SEPARATOR_STR, | ||
563 | fil_ptr); | ||
564 | GNUNET_free (fm); | ||
565 | return buffer; | ||
566 | } | ||
567 | |||
568 | |||
569 | const char * | ||
570 | GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative delta, | ||
571 | int do_round) | ||
572 | { | ||
573 | static GNUNET_THREAD_LOCAL char buf[128]; | ||
574 | const char *unit = /* time unit */ "µs"; | ||
575 | uint64_t dval = delta.rel_value_us; | ||
576 | |||
577 | if (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us == delta.rel_value_us) | ||
578 | return "forever"; | ||
579 | if (0 == delta.rel_value_us) | ||
580 | return "0 ms"; | ||
581 | if (((GNUNET_YES == do_round) && (dval > 5 * 1000)) || (0 == (dval % 1000))) | ||
582 | { | ||
583 | dval = dval / 1000; | ||
584 | unit = /* time unit */ "ms"; | ||
585 | if (((GNUNET_YES == do_round) && (dval > 5 * 1000)) || (0 == (dval % 1000))) | ||
586 | { | ||
587 | dval = dval / 1000; | ||
588 | unit = /* time unit */ "s"; | ||
589 | if (((GNUNET_YES == do_round) && (dval > 5 * 60)) || (0 == (dval % 60))) | ||
590 | { | ||
591 | dval = dval / 60; | ||
592 | unit = /* time unit */ "m"; | ||
593 | if (((GNUNET_YES == do_round) && (dval > 5 * 60)) || (0 == (dval % 60))) | ||
594 | { | ||
595 | dval = dval / 60; | ||
596 | unit = /* time unit */ "h"; | ||
597 | if (((GNUNET_YES == do_round) && (dval > 5 * 24)) || | ||
598 | (0 == (dval % 24))) | ||
599 | { | ||
600 | dval = dval / 24; | ||
601 | if (1 == dval) | ||
602 | unit = /* time unit */ "day"; | ||
603 | else | ||
604 | unit = /* time unit */ "days"; | ||
605 | } | ||
606 | } | ||
607 | } | ||
608 | } | ||
609 | } | ||
610 | GNUNET_snprintf (buf, sizeof(buf), "%llu %s", | ||
611 | (unsigned long long) dval, unit); | ||
612 | return buf; | ||
613 | } | ||
614 | |||
615 | |||
616 | const char * | ||
617 | GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t) | ||
618 | { | ||
619 | static GNUNET_THREAD_LOCAL char buf[255]; | ||
620 | time_t tt; | ||
621 | struct tm *tp; | ||
622 | |||
623 | if (GNUNET_TIME_absolute_is_never (t)) | ||
624 | return "end of time"; | ||
625 | tt = t.abs_value_us / 1000LL / 1000LL; | ||
626 | tp = localtime (&tt); | ||
627 | /* This is hacky, but i don't know a way to detect libc character encoding. | ||
628 | * Just expect utf8 from glibc these days. | ||
629 | * As for msvcrt, use the wide variant, which always returns utf16 | ||
630 | * (otherwise we'd have to detect current codepage or use W32API character | ||
631 | * set conversion routines to convert to UTF8). | ||
632 | */ | ||
633 | strftime (buf, sizeof(buf), "%a %b %d %H:%M:%S %Y", tp); | ||
634 | |||
635 | return buf; | ||
636 | } | ||
637 | |||
638 | |||
639 | const char * | ||
640 | GNUNET_STRINGS_get_short_name (const char *filename) | ||
641 | { | ||
642 | const char *short_fn = filename; | ||
643 | const char *ss; | ||
644 | |||
645 | while (NULL != (ss = strstr (short_fn, DIR_SEPARATOR_STR)) && (ss[1] != '\0')) | ||
646 | short_fn = 1 + ss; | ||
647 | return short_fn; | ||
648 | } | ||
649 | |||
650 | |||
651 | /** | ||
652 | * Get the decoded value corresponding to a character according to Crockford | ||
653 | * Base32 encoding. | ||
654 | * | ||
655 | * @param a a character | ||
656 | * @return corresponding numeric value | ||
657 | */ | ||
658 | static unsigned int | ||
659 | getValue__ (unsigned char a) | ||
660 | { | ||
661 | unsigned int dec; | ||
662 | |||
663 | switch (a) | ||
664 | { | ||
665 | case 'O': | ||
666 | case 'o': | ||
667 | a = '0'; | ||
668 | break; | ||
669 | |||
670 | case 'i': | ||
671 | case 'I': | ||
672 | case 'l': | ||
673 | case 'L': | ||
674 | a = '1'; | ||
675 | break; | ||
676 | |||
677 | /* also consider U to be V */ | ||
678 | case 'u': | ||
679 | case 'U': | ||
680 | a = 'V'; | ||
681 | break; | ||
682 | |||
683 | default: | ||
684 | break; | ||
685 | } | ||
686 | if ((a >= '0') && (a <= '9')) | ||
687 | return a - '0'; | ||
688 | if ((a >= 'a') && (a <= 'z')) | ||
689 | a = toupper (a); | ||
690 | /* return (a - 'a' + 10); */ | ||
691 | dec = 0; | ||
692 | if ((a >= 'A') && (a <= 'Z')) | ||
693 | { | ||
694 | if ('I' < a) | ||
695 | dec++; | ||
696 | if ('L' < a) | ||
697 | dec++; | ||
698 | if ('O' < a) | ||
699 | dec++; | ||
700 | if ('U' < a) | ||
701 | dec++; | ||
702 | return(a - 'A' + 10 - dec); | ||
703 | } | ||
704 | return -1; | ||
705 | } | ||
706 | |||
707 | |||
708 | char * | ||
709 | GNUNET_STRINGS_data_to_string (const void *data, | ||
710 | size_t size, | ||
711 | char *out, | ||
712 | size_t out_size) | ||
713 | { | ||
714 | /** | ||
715 | * 32 characters for encoding | ||
716 | */ | ||
717 | static char *encTable__ = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; | ||
718 | unsigned int wpos; | ||
719 | unsigned int rpos; | ||
720 | unsigned int bits; | ||
721 | unsigned int vbit; | ||
722 | const unsigned char *udata; | ||
723 | |||
724 | GNUNET_assert (size < SIZE_MAX / 8 - 4); | ||
725 | udata = data; | ||
726 | if (out_size < (size * 8 + 4) / 5) | ||
727 | { | ||
728 | GNUNET_break (0); | ||
729 | return NULL; | ||
730 | } | ||
731 | vbit = 0; | ||
732 | wpos = 0; | ||
733 | rpos = 0; | ||
734 | bits = 0; | ||
735 | while ((rpos < size) || (vbit > 0)) | ||
736 | { | ||
737 | if ((rpos < size) && (vbit < 5)) | ||
738 | { | ||
739 | bits = (bits << 8) | udata[rpos++]; /* eat 8 more bits */ | ||
740 | vbit += 8; | ||
741 | } | ||
742 | if (vbit < 5) | ||
743 | { | ||
744 | bits <<= (5 - vbit); /* zero-padding */ | ||
745 | GNUNET_assert (vbit == ((size * 8) % 5)); | ||
746 | vbit = 5; | ||
747 | } | ||
748 | if (wpos >= out_size) | ||
749 | { | ||
750 | GNUNET_break (0); | ||
751 | return NULL; | ||
752 | } | ||
753 | out[wpos++] = encTable__[(bits >> (vbit - 5)) & 31]; | ||
754 | vbit -= 5; | ||
755 | } | ||
756 | GNUNET_assert (0 == vbit); | ||
757 | if (wpos < out_size) | ||
758 | out[wpos] = '\0'; | ||
759 | return &out[wpos]; | ||
760 | } | ||
761 | |||
762 | |||
763 | char * | ||
764 | GNUNET_STRINGS_data_to_string_alloc (const void *buf, size_t size) | ||
765 | { | ||
766 | char *str_buf; | ||
767 | size_t len = size * 8; | ||
768 | char *end; | ||
769 | |||
770 | if (len % 5 > 0) | ||
771 | len += 5 - len % 5; | ||
772 | len /= 5; | ||
773 | str_buf = GNUNET_malloc (len + 1); | ||
774 | end = GNUNET_STRINGS_data_to_string (buf, | ||
775 | size, | ||
776 | str_buf, | ||
777 | len); | ||
778 | if (NULL == end) | ||
779 | { | ||
780 | GNUNET_free (str_buf); | ||
781 | return NULL; | ||
782 | } | ||
783 | *end = '\0'; | ||
784 | return str_buf; | ||
785 | } | ||
786 | |||
787 | |||
788 | enum GNUNET_GenericReturnValue | ||
789 | GNUNET_STRINGS_string_to_data (const char *enc, | ||
790 | size_t enclen, | ||
791 | void *out, | ||
792 | size_t out_size) | ||
793 | { | ||
794 | size_t rpos; | ||
795 | size_t wpos; | ||
796 | unsigned int bits; | ||
797 | unsigned int vbit; | ||
798 | int ret; | ||
799 | int shift; | ||
800 | unsigned char *uout; | ||
801 | size_t encoded_len; | ||
802 | |||
803 | if (0 == enclen) | ||
804 | { | ||
805 | if (0 == out_size) | ||
806 | return GNUNET_OK; | ||
807 | return GNUNET_SYSERR; | ||
808 | } | ||
809 | GNUNET_assert (out_size < SIZE_MAX / 8); | ||
810 | encoded_len = out_size * 8; | ||
811 | uout = out; | ||
812 | wpos = out_size; | ||
813 | rpos = enclen; | ||
814 | if ((encoded_len % 5) > 0) | ||
815 | { | ||
816 | vbit = encoded_len % 5; /* padding! */ | ||
817 | shift = 5 - vbit; | ||
818 | bits = (ret = getValue__ (enc[--rpos])) >> shift; | ||
819 | } | ||
820 | else | ||
821 | { | ||
822 | vbit = 5; | ||
823 | shift = 0; | ||
824 | bits = (ret = getValue__ (enc[--rpos])); | ||
825 | } | ||
826 | if ((encoded_len + shift) / 5 != enclen) | ||
827 | return GNUNET_SYSERR; | ||
828 | if (-1 == ret) | ||
829 | return GNUNET_SYSERR; | ||
830 | while (wpos > 0) | ||
831 | { | ||
832 | if (0 == rpos) | ||
833 | { | ||
834 | GNUNET_break (0); | ||
835 | return GNUNET_SYSERR; | ||
836 | } | ||
837 | bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits; | ||
838 | if (-1 == ret) | ||
839 | return GNUNET_SYSERR; | ||
840 | vbit += 5; | ||
841 | if (vbit >= 8) | ||
842 | { | ||
843 | uout[--wpos] = (unsigned char) bits; | ||
844 | bits >>= 8; | ||
845 | vbit -= 8; | ||
846 | } | ||
847 | } | ||
848 | if ((0 != rpos) || (0 != vbit)) | ||
849 | return GNUNET_SYSERR; | ||
850 | return GNUNET_OK; | ||
851 | } | ||
852 | |||
853 | |||
854 | enum GNUNET_GenericReturnValue | ||
855 | GNUNET_STRINGS_string_to_data_alloc (const char *enc, | ||
856 | size_t enclen, | ||
857 | void **out, | ||
858 | size_t *out_size) | ||
859 | { | ||
860 | size_t size; | ||
861 | void *data; | ||
862 | int res; | ||
863 | |||
864 | size = (enclen * 5) / 8; | ||
865 | if (size >= GNUNET_MAX_MALLOC_CHECKED) | ||
866 | { | ||
867 | GNUNET_break_op (0); | ||
868 | return GNUNET_SYSERR; | ||
869 | } | ||
870 | data = GNUNET_malloc (size); | ||
871 | res = GNUNET_STRINGS_string_to_data (enc, | ||
872 | enclen, | ||
873 | data, | ||
874 | size); | ||
875 | if ( (0 < size) && | ||
876 | (GNUNET_OK != res) ) | ||
877 | { | ||
878 | size--; | ||
879 | res = GNUNET_STRINGS_string_to_data (enc, | ||
880 | enclen, | ||
881 | data, | ||
882 | size); | ||
883 | } | ||
884 | if (GNUNET_OK != res) | ||
885 | { | ||
886 | GNUNET_break_op (0); | ||
887 | GNUNET_free (data); | ||
888 | return GNUNET_SYSERR; | ||
889 | } | ||
890 | *out = data; | ||
891 | *out_size = size; | ||
892 | return GNUNET_OK; | ||
893 | } | ||
894 | |||
895 | |||
896 | enum GNUNET_GenericReturnValue | ||
897 | GNUNET_STRINGS_parse_uri (const char *path, | ||
898 | char **scheme_part, | ||
899 | const char **path_part) | ||
900 | { | ||
901 | size_t len; | ||
902 | size_t i; | ||
903 | int end; | ||
904 | int pp_state = 0; | ||
905 | const char *post_scheme_part = NULL; | ||
906 | |||
907 | len = strlen (path); | ||
908 | for (end = 0, i = 0; ! end && i < len; i++) | ||
909 | { | ||
910 | switch (pp_state) | ||
911 | { | ||
912 | case 0: | ||
913 | if ((path[i] == ':') && (i > 0)) | ||
914 | { | ||
915 | pp_state += 1; | ||
916 | continue; | ||
917 | } | ||
918 | if (! (((path[i] >= 'A') && (path[i] <= 'Z') ) || | ||
919 | ((path[i] >= 'a') && (path[i] <= 'z') ) || | ||
920 | ((path[i] >= '0') && (path[i] <= '9') ) || (path[i] == '+') || | ||
921 | (path[i] == '-') || (path[i] == '.'))) | ||
922 | end = 1; | ||
923 | break; | ||
924 | |||
925 | case 1: | ||
926 | case 2: | ||
927 | if (path[i] == '/') | ||
928 | { | ||
929 | pp_state += 1; | ||
930 | continue; | ||
931 | } | ||
932 | end = 1; | ||
933 | break; | ||
934 | |||
935 | case 3: | ||
936 | post_scheme_part = &path[i]; | ||
937 | end = 1; | ||
938 | break; | ||
939 | |||
940 | default: | ||
941 | end = 1; | ||
942 | } | ||
943 | } | ||
944 | if (post_scheme_part == NULL) | ||
945 | return GNUNET_NO; | ||
946 | if (scheme_part) | ||
947 | { | ||
948 | *scheme_part = GNUNET_malloc (post_scheme_part - path + 1); | ||
949 | GNUNET_memcpy (*scheme_part, path, post_scheme_part - path); | ||
950 | (*scheme_part)[post_scheme_part - path] = '\0'; | ||
951 | } | ||
952 | if (path_part) | ||
953 | *path_part = post_scheme_part; | ||
954 | return GNUNET_YES; | ||
955 | } | ||
956 | |||
957 | |||
958 | enum GNUNET_GenericReturnValue | ||
959 | GNUNET_STRINGS_path_is_absolute (const char *filename, | ||
960 | int can_be_uri, | ||
961 | int *r_is_uri, | ||
962 | char **r_uri_scheme) | ||
963 | { | ||
964 | const char *post_scheme_path; | ||
965 | int is_uri; | ||
966 | char *uri; | ||
967 | /* consider POSIX paths to be absolute too, even on W32, | ||
968 | * as plibc expansion will fix them for us. | ||
969 | */ | ||
970 | if (filename[0] == '/') | ||
971 | return GNUNET_YES; | ||
972 | if (can_be_uri) | ||
973 | { | ||
974 | is_uri = GNUNET_STRINGS_parse_uri (filename, &uri, &post_scheme_path); | ||
975 | if (r_is_uri) | ||
976 | *r_is_uri = is_uri; | ||
977 | if (is_uri) | ||
978 | { | ||
979 | if (r_uri_scheme) | ||
980 | *r_uri_scheme = uri; | ||
981 | else | ||
982 | GNUNET_free (uri); | ||
983 | |||
984 | return GNUNET_STRINGS_path_is_absolute (post_scheme_path, | ||
985 | GNUNET_NO, | ||
986 | NULL, | ||
987 | NULL); | ||
988 | } | ||
989 | } | ||
990 | else | ||
991 | { | ||
992 | if (r_is_uri) | ||
993 | *r_is_uri = GNUNET_NO; | ||
994 | } | ||
995 | |||
996 | return GNUNET_NO; | ||
997 | } | ||
998 | |||
999 | |||
1000 | enum GNUNET_GenericReturnValue | ||
1001 | GNUNET_STRINGS_check_filename (const char *filename, | ||
1002 | enum GNUNET_STRINGS_FilenameCheck checks) | ||
1003 | { | ||
1004 | struct stat st; | ||
1005 | |||
1006 | if ((NULL == filename) || (filename[0] == '\0')) | ||
1007 | return GNUNET_SYSERR; | ||
1008 | if (0 != (checks & GNUNET_STRINGS_CHECK_IS_ABSOLUTE)) | ||
1009 | if (! GNUNET_STRINGS_path_is_absolute (filename, GNUNET_NO, NULL, NULL)) | ||
1010 | return GNUNET_NO; | ||
1011 | if (0 != (checks | ||
1012 | & (GNUNET_STRINGS_CHECK_EXISTS | GNUNET_STRINGS_CHECK_IS_DIRECTORY | ||
1013 | | GNUNET_STRINGS_CHECK_IS_LINK))) | ||
1014 | { | ||
1015 | if (0 != lstat (filename, &st)) | ||
1016 | { | ||
1017 | if (0 != (checks & GNUNET_STRINGS_CHECK_EXISTS)) | ||
1018 | return GNUNET_NO; | ||
1019 | else | ||
1020 | return GNUNET_SYSERR; | ||
1021 | } | ||
1022 | } | ||
1023 | if (0 != (checks & GNUNET_STRINGS_CHECK_IS_DIRECTORY)) | ||
1024 | if (! S_ISDIR (st.st_mode)) | ||
1025 | return GNUNET_NO; | ||
1026 | if (0 != (checks & GNUNET_STRINGS_CHECK_IS_LINK)) | ||
1027 | if (! S_ISLNK (st.st_mode)) | ||
1028 | return GNUNET_NO; | ||
1029 | return GNUNET_YES; | ||
1030 | } | ||
1031 | |||
1032 | |||
1033 | enum GNUNET_GenericReturnValue | ||
1034 | GNUNET_STRINGS_to_address_ipv6 (const char *zt_addr, | ||
1035 | uint16_t addrlen, | ||
1036 | struct sockaddr_in6 *r_buf) | ||
1037 | { | ||
1038 | char zbuf[addrlen + 1]; | ||
1039 | int ret; | ||
1040 | char *port_colon; | ||
1041 | unsigned int port; | ||
1042 | char dummy[2]; | ||
1043 | |||
1044 | if (addrlen < 6) | ||
1045 | return GNUNET_SYSERR; | ||
1046 | GNUNET_memcpy (zbuf, zt_addr, addrlen); | ||
1047 | if ('[' != zbuf[0]) | ||
1048 | { | ||
1049 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1050 | _ ("IPv6 address did not start with `['\n")); | ||
1051 | return GNUNET_SYSERR; | ||
1052 | } | ||
1053 | zbuf[addrlen] = '\0'; | ||
1054 | port_colon = strrchr (zbuf, ':'); | ||
1055 | if (NULL == port_colon) | ||
1056 | { | ||
1057 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1058 | _ ("IPv6 address did contain ':' to separate port number\n")); | ||
1059 | return GNUNET_SYSERR; | ||
1060 | } | ||
1061 | if (']' != *(port_colon - 1)) | ||
1062 | { | ||
1063 | GNUNET_log ( | ||
1064 | GNUNET_ERROR_TYPE_WARNING, | ||
1065 | _ ("IPv6 address did contain ']' before ':' to separate port number\n")); | ||
1066 | return GNUNET_SYSERR; | ||
1067 | } | ||
1068 | ret = sscanf (port_colon, ":%u%1s", &port, dummy); | ||
1069 | if ((1 != ret) || (port > 65535)) | ||
1070 | { | ||
1071 | GNUNET_log ( | ||
1072 | GNUNET_ERROR_TYPE_WARNING, | ||
1073 | _ ("IPv6 address did contain a valid port number after the last ':'\n")); | ||
1074 | return GNUNET_SYSERR; | ||
1075 | } | ||
1076 | *(port_colon - 1) = '\0'; | ||
1077 | memset (r_buf, 0, sizeof(struct sockaddr_in6)); | ||
1078 | ret = inet_pton (AF_INET6, &zbuf[1], &r_buf->sin6_addr); | ||
1079 | if (ret <= 0) | ||
1080 | { | ||
1081 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1082 | _ ("Invalid IPv6 address `%s': %s\n"), | ||
1083 | &zbuf[1], | ||
1084 | strerror (errno)); | ||
1085 | return GNUNET_SYSERR; | ||
1086 | } | ||
1087 | r_buf->sin6_port = htons (port); | ||
1088 | r_buf->sin6_family = AF_INET6; | ||
1089 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
1090 | r_buf->sin6_len = (u_char) sizeof(struct sockaddr_in6); | ||
1091 | #endif | ||
1092 | return GNUNET_OK; | ||
1093 | } | ||
1094 | |||
1095 | |||
1096 | enum GNUNET_GenericReturnValue | ||
1097 | GNUNET_STRINGS_to_address_ipv4 (const char *zt_addr, | ||
1098 | uint16_t addrlen, | ||
1099 | struct sockaddr_in *r_buf) | ||
1100 | { | ||
1101 | unsigned int temps[4]; | ||
1102 | unsigned int port; | ||
1103 | unsigned int cnt; | ||
1104 | char dummy[2]; | ||
1105 | |||
1106 | if (addrlen < 9) | ||
1107 | return GNUNET_SYSERR; | ||
1108 | cnt = sscanf (zt_addr, | ||
1109 | "%u.%u.%u.%u:%u%1s", | ||
1110 | &temps[0], | ||
1111 | &temps[1], | ||
1112 | &temps[2], | ||
1113 | &temps[3], | ||
1114 | &port, | ||
1115 | dummy); | ||
1116 | if (5 != cnt) | ||
1117 | return GNUNET_SYSERR; | ||
1118 | for (cnt = 0; cnt < 4; cnt++) | ||
1119 | if (temps[cnt] > 0xFF) | ||
1120 | return GNUNET_SYSERR; | ||
1121 | if (port > 65535) | ||
1122 | return GNUNET_SYSERR; | ||
1123 | r_buf->sin_family = AF_INET; | ||
1124 | r_buf->sin_port = htons (port); | ||
1125 | r_buf->sin_addr.s_addr = | ||
1126 | htonl ((temps[0] << 24) + (temps[1] << 16) + (temps[2] << 8) + temps[3]); | ||
1127 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
1128 | r_buf->sin_len = (u_char) sizeof(struct sockaddr_in); | ||
1129 | #endif | ||
1130 | return GNUNET_OK; | ||
1131 | } | ||
1132 | |||
1133 | |||
1134 | enum GNUNET_GenericReturnValue | ||
1135 | GNUNET_STRINGS_to_address_ip (const char *addr, | ||
1136 | uint16_t addrlen, | ||
1137 | struct sockaddr_storage *r_buf) | ||
1138 | { | ||
1139 | if (addr[0] == '[') | ||
1140 | return GNUNET_STRINGS_to_address_ipv6 (addr, | ||
1141 | addrlen, | ||
1142 | (struct sockaddr_in6 *) r_buf); | ||
1143 | return GNUNET_STRINGS_to_address_ipv4 (addr, | ||
1144 | addrlen, | ||
1145 | (struct sockaddr_in *) r_buf); | ||
1146 | } | ||
1147 | |||
1148 | |||
1149 | size_t | ||
1150 | GNUNET_STRINGS_parse_socket_addr (const char *addr, | ||
1151 | uint8_t *af, | ||
1152 | struct sockaddr **sa) | ||
1153 | { | ||
1154 | char *cp = GNUNET_strdup (addr); | ||
1155 | |||
1156 | *af = AF_UNSPEC; | ||
1157 | if ('[' == *addr) | ||
1158 | { | ||
1159 | /* IPv6 */ | ||
1160 | *sa = GNUNET_malloc (sizeof(struct sockaddr_in6)); | ||
1161 | if (GNUNET_OK != | ||
1162 | GNUNET_STRINGS_to_address_ipv6 (cp, | ||
1163 | strlen (cp), | ||
1164 | (struct sockaddr_in6 *) *sa)) | ||
1165 | { | ||
1166 | GNUNET_free (*sa); | ||
1167 | *sa = NULL; | ||
1168 | GNUNET_free (cp); | ||
1169 | return 0; | ||
1170 | } | ||
1171 | *af = AF_INET6; | ||
1172 | GNUNET_free (cp); | ||
1173 | return sizeof(struct sockaddr_in6); | ||
1174 | } | ||
1175 | else | ||
1176 | { | ||
1177 | /* IPv4 */ | ||
1178 | *sa = GNUNET_malloc (sizeof(struct sockaddr_in)); | ||
1179 | if (GNUNET_OK != | ||
1180 | GNUNET_STRINGS_to_address_ipv4 (cp, | ||
1181 | strlen (cp), | ||
1182 | (struct sockaddr_in *) *sa)) | ||
1183 | { | ||
1184 | GNUNET_free (*sa); | ||
1185 | *sa = NULL; | ||
1186 | GNUNET_free (cp); | ||
1187 | return 0; | ||
1188 | } | ||
1189 | *af = AF_INET; | ||
1190 | GNUNET_free (cp); | ||
1191 | return sizeof(struct sockaddr_in); | ||
1192 | } | ||
1193 | } | ||
1194 | |||
1195 | |||
1196 | /** | ||
1197 | * Makes a copy of argv that consists of a single memory chunk that can be | ||
1198 | * freed with a single call to GNUNET_free(); | ||
1199 | */ | ||
1200 | static char *const * | ||
1201 | _make_continuous_arg_copy (int argc, char *const *argv) | ||
1202 | { | ||
1203 | size_t argvsize = 0; | ||
1204 | char **new_argv; | ||
1205 | char *p; | ||
1206 | |||
1207 | for (int i = 0; i < argc; i++) | ||
1208 | argvsize += strlen (argv[i]) + 1 + sizeof(char *); | ||
1209 | new_argv = GNUNET_malloc (argvsize + sizeof(char *)); | ||
1210 | p = (char *) &new_argv[argc + 1]; | ||
1211 | for (int i = 0; i < argc; i++) | ||
1212 | { | ||
1213 | new_argv[i] = p; | ||
1214 | strcpy (p, argv[i]); | ||
1215 | p += strlen (argv[i]) + 1; | ||
1216 | } | ||
1217 | new_argv[argc] = NULL; | ||
1218 | return (char *const *) new_argv; | ||
1219 | } | ||
1220 | |||
1221 | |||
1222 | enum GNUNET_GenericReturnValue | ||
1223 | GNUNET_STRINGS_get_utf8_args (int argc, | ||
1224 | char *const *argv, | ||
1225 | int *u8argc, | ||
1226 | char *const **u8argv) | ||
1227 | { | ||
1228 | char *const *new_argv = | ||
1229 | (char *const *) _make_continuous_arg_copy (argc, argv); | ||
1230 | *u8argv = new_argv; | ||
1231 | *u8argc = argc; | ||
1232 | return GNUNET_OK; | ||
1233 | } | ||
1234 | |||
1235 | |||
1236 | /** | ||
1237 | * Parse the given port policy. The format is | ||
1238 | * "[!]SPORT[-DPORT]". | ||
1239 | * | ||
1240 | * @param port_policy string to parse | ||
1241 | * @param pp policy to fill in | ||
1242 | * @return #GNUNET_OK on success, #GNUNET_SYSERR if the | ||
1243 | * @a port_policy is malformed | ||
1244 | */ | ||
1245 | static enum GNUNET_GenericReturnValue | ||
1246 | parse_port_policy (const char *port_policy, | ||
1247 | struct GNUNET_STRINGS_PortPolicy *pp) | ||
1248 | { | ||
1249 | const char *pos; | ||
1250 | int s; | ||
1251 | int e; | ||
1252 | char eol[2]; | ||
1253 | |||
1254 | pos = port_policy; | ||
1255 | if ('!' == *pos) | ||
1256 | { | ||
1257 | pp->negate_portrange = GNUNET_YES; | ||
1258 | pos++; | ||
1259 | } | ||
1260 | if (2 == sscanf (pos, "%u-%u%1s", &s, &e, eol)) | ||
1261 | { | ||
1262 | if ((0 == s) || (s > 0xFFFF) || (e < s) || (e > 0xFFFF)) | ||
1263 | { | ||
1264 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Port not in range\n")); | ||
1265 | return GNUNET_SYSERR; | ||
1266 | } | ||
1267 | pp->start_port = (uint16_t) s; | ||
1268 | pp->end_port = (uint16_t) e; | ||
1269 | return GNUNET_OK; | ||
1270 | } | ||
1271 | if (1 == sscanf (pos, "%u%1s", &s, eol)) | ||
1272 | { | ||
1273 | if ((0 == s) || (s > 0xFFFF)) | ||
1274 | { | ||
1275 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Port not in range\n")); | ||
1276 | return GNUNET_SYSERR; | ||
1277 | } | ||
1278 | |||
1279 | pp->start_port = (uint16_t) s; | ||
1280 | pp->end_port = (uint16_t) s; | ||
1281 | return GNUNET_OK; | ||
1282 | } | ||
1283 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1284 | _ ("Malformed port policy `%s'\n"), | ||
1285 | port_policy); | ||
1286 | return GNUNET_SYSERR; | ||
1287 | } | ||
1288 | |||
1289 | |||
1290 | struct GNUNET_STRINGS_IPv4NetworkPolicy * | ||
1291 | GNUNET_STRINGS_parse_ipv4_policy (const char *routeListX) | ||
1292 | { | ||
1293 | unsigned int count; | ||
1294 | unsigned int i; | ||
1295 | unsigned int j; | ||
1296 | unsigned int len; | ||
1297 | int cnt; | ||
1298 | unsigned int pos; | ||
1299 | unsigned int temps[8]; | ||
1300 | int slash; | ||
1301 | struct GNUNET_STRINGS_IPv4NetworkPolicy *result; | ||
1302 | int colon; | ||
1303 | int end; | ||
1304 | char *routeList; | ||
1305 | char dummy[2]; | ||
1306 | |||
1307 | if (NULL == routeListX) | ||
1308 | return NULL; | ||
1309 | len = strlen (routeListX); | ||
1310 | if (0 == len) | ||
1311 | return NULL; | ||
1312 | routeList = GNUNET_strdup (routeListX); | ||
1313 | count = 0; | ||
1314 | for (i = 0; i < len; i++) | ||
1315 | if (routeList[i] == ';') | ||
1316 | count++; | ||
1317 | result = GNUNET_malloc (sizeof(struct GNUNET_STRINGS_IPv4NetworkPolicy) | ||
1318 | * (count + 1)); | ||
1319 | i = 0; | ||
1320 | pos = 0; | ||
1321 | while (i < count) | ||
1322 | { | ||
1323 | for (colon = pos; ':' != routeList[colon]; colon++) | ||
1324 | if ((';' == routeList[colon]) || ('\0' == routeList[colon])) | ||
1325 | break; | ||
1326 | for (end = colon; ';' != routeList[end]; end++) | ||
1327 | if ('\0' == routeList[end]) | ||
1328 | break; | ||
1329 | if ('\0' == routeList[end]) | ||
1330 | break; | ||
1331 | routeList[end] = '\0'; | ||
1332 | if (':' == routeList[colon]) | ||
1333 | { | ||
1334 | routeList[colon] = '\0'; | ||
1335 | if (GNUNET_OK != parse_port_policy (&routeList[colon + 1], &result[i].pp)) | ||
1336 | break; | ||
1337 | } | ||
1338 | cnt = sscanf (&routeList[pos], | ||
1339 | "%u.%u.%u.%u/%u.%u.%u.%u%1s", | ||
1340 | &temps[0], | ||
1341 | &temps[1], | ||
1342 | &temps[2], | ||
1343 | &temps[3], | ||
1344 | &temps[4], | ||
1345 | &temps[5], | ||
1346 | &temps[6], | ||
1347 | &temps[7], | ||
1348 | dummy); | ||
1349 | if (8 == cnt) | ||
1350 | { | ||
1351 | for (j = 0; j < 8; j++) | ||
1352 | if (temps[j] > 0xFF) | ||
1353 | { | ||
1354 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1355 | _ ("Invalid format for IP: `%s'\n"), | ||
1356 | &routeList[pos]); | ||
1357 | GNUNET_free (result); | ||
1358 | GNUNET_free (routeList); | ||
1359 | return NULL; | ||
1360 | } | ||
1361 | result[i].network.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16) | ||
1362 | + (temps[2] << 8) + temps[3]); | ||
1363 | result[i].netmask.s_addr = htonl ((temps[4] << 24) + (temps[5] << 16) | ||
1364 | + (temps[6] << 8) + temps[7]); | ||
1365 | pos = end + 1; | ||
1366 | i++; | ||
1367 | continue; | ||
1368 | } | ||
1369 | /* try second notation */ | ||
1370 | cnt = sscanf (&routeList[pos], | ||
1371 | "%u.%u.%u.%u/%u%1s", | ||
1372 | &temps[0], | ||
1373 | &temps[1], | ||
1374 | &temps[2], | ||
1375 | &temps[3], | ||
1376 | &slash, | ||
1377 | dummy); | ||
1378 | if (5 == cnt) | ||
1379 | { | ||
1380 | for (j = 0; j < 4; j++) | ||
1381 | if (temps[j] > 0xFF) | ||
1382 | { | ||
1383 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1384 | _ ("Invalid format for IP: `%s'\n"), | ||
1385 | &routeList[pos]); | ||
1386 | GNUNET_free (result); | ||
1387 | GNUNET_free (routeList); | ||
1388 | return NULL; | ||
1389 | } | ||
1390 | result[i].network.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16) | ||
1391 | + (temps[2] << 8) + temps[3]); | ||
1392 | if ((slash <= 32) && (slash >= 0)) | ||
1393 | { | ||
1394 | result[i].netmask.s_addr = 0; | ||
1395 | while (slash > 0) | ||
1396 | { | ||
1397 | result[i].netmask.s_addr = | ||
1398 | (result[i].netmask.s_addr >> 1) + 0x80000000; | ||
1399 | slash--; | ||
1400 | } | ||
1401 | result[i].netmask.s_addr = htonl (result[i].netmask.s_addr); | ||
1402 | pos = end + 1; | ||
1403 | i++; | ||
1404 | continue; | ||
1405 | } | ||
1406 | else | ||
1407 | { | ||
1408 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1409 | _ ("Invalid network notation ('/%d' is not legal in IPv4 CIDR)."), | ||
1410 | slash); | ||
1411 | GNUNET_free (result); | ||
1412 | GNUNET_free (routeList); | ||
1413 | return NULL; /* error */ | ||
1414 | } | ||
1415 | } | ||
1416 | /* try third notation */ | ||
1417 | slash = 32; | ||
1418 | cnt = sscanf (&routeList[pos], | ||
1419 | "%u.%u.%u.%u%1s", | ||
1420 | &temps[0], | ||
1421 | &temps[1], | ||
1422 | &temps[2], | ||
1423 | &temps[3], | ||
1424 | dummy); | ||
1425 | if (4 == cnt) | ||
1426 | { | ||
1427 | for (j = 0; j < 4; j++) | ||
1428 | if (temps[j] > 0xFF) | ||
1429 | { | ||
1430 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1431 | _ ("Invalid format for IP: `%s'\n"), | ||
1432 | &routeList[pos]); | ||
1433 | GNUNET_free (result); | ||
1434 | GNUNET_free (routeList); | ||
1435 | return NULL; | ||
1436 | } | ||
1437 | result[i].network.s_addr = htonl ((temps[0] << 24) + (temps[1] << 16) | ||
1438 | + (temps[2] << 8) + temps[3]); | ||
1439 | result[i].netmask.s_addr = 0; | ||
1440 | while (slash > 0) | ||
1441 | { | ||
1442 | result[i].netmask.s_addr = (result[i].netmask.s_addr >> 1) + 0x80000000; | ||
1443 | slash--; | ||
1444 | } | ||
1445 | result[i].netmask.s_addr = htonl (result[i].netmask.s_addr); | ||
1446 | pos = end + 1; | ||
1447 | i++; | ||
1448 | continue; | ||
1449 | } | ||
1450 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1451 | _ ("Invalid format for IP: `%s'\n"), | ||
1452 | &routeList[pos]); | ||
1453 | GNUNET_free (result); | ||
1454 | GNUNET_free (routeList); | ||
1455 | return NULL; /* error */ | ||
1456 | } | ||
1457 | if (pos < strlen (routeList)) | ||
1458 | { | ||
1459 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1460 | _ ("Invalid format: `%s'\n"), | ||
1461 | &routeListX[pos]); | ||
1462 | GNUNET_free (result); | ||
1463 | GNUNET_free (routeList); | ||
1464 | return NULL; /* oops */ | ||
1465 | } | ||
1466 | GNUNET_free (routeList); | ||
1467 | return result; /* ok */ | ||
1468 | } | ||
1469 | |||
1470 | |||
1471 | struct GNUNET_STRINGS_IPv6NetworkPolicy * | ||
1472 | GNUNET_STRINGS_parse_ipv6_policy (const char *routeListX) | ||
1473 | { | ||
1474 | unsigned int count; | ||
1475 | unsigned int i; | ||
1476 | unsigned int len; | ||
1477 | unsigned int pos; | ||
1478 | int start; | ||
1479 | int slash; | ||
1480 | int ret; | ||
1481 | char *routeList; | ||
1482 | struct GNUNET_STRINGS_IPv6NetworkPolicy *result; | ||
1483 | unsigned int bits; | ||
1484 | unsigned int off; | ||
1485 | int save; | ||
1486 | int colon; | ||
1487 | char dummy[2]; | ||
1488 | |||
1489 | if (NULL == routeListX) | ||
1490 | return NULL; | ||
1491 | len = strlen (routeListX); | ||
1492 | if (0 == len) | ||
1493 | return NULL; | ||
1494 | routeList = GNUNET_strdup (routeListX); | ||
1495 | count = 0; | ||
1496 | for (i = 0; i < len; i++) | ||
1497 | if (';' == routeList[i]) | ||
1498 | count++; | ||
1499 | if (';' != routeList[len - 1]) | ||
1500 | { | ||
1501 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1502 | _ ("Invalid network notation (does not end with ';': `%s')\n"), | ||
1503 | routeList); | ||
1504 | GNUNET_free (routeList); | ||
1505 | return NULL; | ||
1506 | } | ||
1507 | |||
1508 | result = GNUNET_malloc (sizeof(struct GNUNET_STRINGS_IPv6NetworkPolicy) | ||
1509 | * (count + 1)); | ||
1510 | i = 0; | ||
1511 | pos = 0; | ||
1512 | while (i < count) | ||
1513 | { | ||
1514 | start = pos; | ||
1515 | while (';' != routeList[pos]) | ||
1516 | pos++; | ||
1517 | slash = pos; | ||
1518 | while ((slash >= start) && (routeList[slash] != '/')) | ||
1519 | slash--; | ||
1520 | |||
1521 | if (slash < start) | ||
1522 | { | ||
1523 | memset (&result[i].netmask, 0xFF, sizeof(struct in6_addr)); | ||
1524 | slash = pos; | ||
1525 | } | ||
1526 | else | ||
1527 | { | ||
1528 | routeList[pos] = '\0'; | ||
1529 | for (colon = pos; ':' != routeList[colon]; colon--) | ||
1530 | if ('/' == routeList[colon]) | ||
1531 | break; | ||
1532 | if (':' == routeList[colon]) | ||
1533 | { | ||
1534 | routeList[colon] = '\0'; | ||
1535 | if (GNUNET_OK != | ||
1536 | parse_port_policy (&routeList[colon + 1], &result[i].pp)) | ||
1537 | { | ||
1538 | GNUNET_free (result); | ||
1539 | GNUNET_free (routeList); | ||
1540 | return NULL; | ||
1541 | } | ||
1542 | } | ||
1543 | ret = inet_pton (AF_INET6, &routeList[slash + 1], &result[i].netmask); | ||
1544 | if (ret <= 0) | ||
1545 | { | ||
1546 | save = errno; | ||
1547 | if ((1 != sscanf (&routeList[slash + 1], "%u%1s", &bits, dummy)) || | ||
1548 | (bits > 128)) | ||
1549 | { | ||
1550 | if (0 == ret) | ||
1551 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1552 | _ ("Wrong format `%s' for netmask\n"), | ||
1553 | &routeList[slash + 1]); | ||
1554 | else | ||
1555 | { | ||
1556 | errno = save; | ||
1557 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_pton"); | ||
1558 | } | ||
1559 | GNUNET_free (result); | ||
1560 | GNUNET_free (routeList); | ||
1561 | return NULL; | ||
1562 | } | ||
1563 | off = 0; | ||
1564 | while (bits > 8) | ||
1565 | { | ||
1566 | result[i].netmask.s6_addr[off++] = 0xFF; | ||
1567 | bits -= 8; | ||
1568 | } | ||
1569 | while (bits > 0) | ||
1570 | { | ||
1571 | result[i].netmask.s6_addr[off] = | ||
1572 | (result[i].netmask.s6_addr[off] >> 1) + 0x80; | ||
1573 | bits--; | ||
1574 | } | ||
1575 | } | ||
1576 | } | ||
1577 | routeList[slash] = '\0'; | ||
1578 | ret = inet_pton (AF_INET6, &routeList[start], &result[i].network); | ||
1579 | if (ret <= 0) | ||
1580 | { | ||
1581 | if (0 == ret) | ||
1582 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
1583 | _ ("Wrong format `%s' for network\n"), | ||
1584 | &routeList[slash + 1]); | ||
1585 | else | ||
1586 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "inet_pton"); | ||
1587 | GNUNET_free (result); | ||
1588 | GNUNET_free (routeList); | ||
1589 | return NULL; | ||
1590 | } | ||
1591 | pos++; | ||
1592 | i++; | ||
1593 | } | ||
1594 | GNUNET_free (routeList); | ||
1595 | return result; | ||
1596 | } | ||
1597 | |||
1598 | |||
1599 | /** ******************** Base64 encoding ***********/ | ||
1600 | |||
1601 | #define FILLCHAR '=' | ||
1602 | static char *cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
1603 | "abcdefghijklmnopqrstuvwxyz" | ||
1604 | "0123456789+/"; | ||
1605 | |||
1606 | |||
1607 | size_t | ||
1608 | GNUNET_STRINGS_base64_encode (const void *in, | ||
1609 | size_t len, | ||
1610 | char **output) | ||
1611 | { | ||
1612 | const unsigned char *data = in; | ||
1613 | size_t ret; | ||
1614 | char *opt; | ||
1615 | |||
1616 | ret = 0; | ||
1617 | GNUNET_assert (len < SIZE_MAX / 4 * 3); | ||
1618 | opt = GNUNET_malloc (2 + (len * 4 / 3) + 8); | ||
1619 | for (size_t i = 0; i < len; ++i) | ||
1620 | { | ||
1621 | char c; | ||
1622 | |||
1623 | c = (data[i] >> 2) & 0x3f; | ||
1624 | opt[ret++] = cvt[(int) c]; | ||
1625 | c = (data[i] << 4) & 0x3f; | ||
1626 | if (++i < len) | ||
1627 | c |= (data[i] >> 4) & 0x0f; | ||
1628 | opt[ret++] = cvt[(int) c]; | ||
1629 | if (i < len) | ||
1630 | { | ||
1631 | c = (data[i] << 2) & 0x3f; | ||
1632 | if (++i < len) | ||
1633 | c |= (data[i] >> 6) & 0x03; | ||
1634 | opt[ret++] = cvt[(int) c]; | ||
1635 | } | ||
1636 | else | ||
1637 | { | ||
1638 | ++i; | ||
1639 | opt[ret++] = FILLCHAR; | ||
1640 | } | ||
1641 | if (i < len) | ||
1642 | { | ||
1643 | c = data[i] & 0x3f; | ||
1644 | opt[ret++] = cvt[(int) c]; | ||
1645 | } | ||
1646 | else | ||
1647 | { | ||
1648 | opt[ret++] = FILLCHAR; | ||
1649 | } | ||
1650 | } | ||
1651 | *output = opt; | ||
1652 | return ret; | ||
1653 | } | ||
1654 | |||
1655 | |||
1656 | size_t | ||
1657 | GNUNET_STRINGS_base64url_encode (const void *in, | ||
1658 | size_t len, | ||
1659 | char **output) | ||
1660 | { | ||
1661 | char *enc; | ||
1662 | size_t pos; | ||
1663 | |||
1664 | GNUNET_STRINGS_base64_encode (in, | ||
1665 | len, | ||
1666 | output); | ||
1667 | enc = *output; | ||
1668 | /* Replace with correct characters for base64url */ | ||
1669 | pos = 0; | ||
1670 | while ('\0' != enc[pos]) | ||
1671 | { | ||
1672 | if ('+' == enc[pos]) | ||
1673 | enc[pos] = '-'; | ||
1674 | if ('/' == enc[pos]) | ||
1675 | enc[pos] = '_'; | ||
1676 | if ('=' == enc[pos]) | ||
1677 | { | ||
1678 | enc[pos] = '\0'; | ||
1679 | break; | ||
1680 | } | ||
1681 | pos++; | ||
1682 | } | ||
1683 | return strlen (enc); | ||
1684 | } | ||
1685 | |||
1686 | |||
1687 | #define cvtfind(a) \ | ||
1688 | ((((a) >= 'A') && ((a) <= 'Z')) \ | ||
1689 | ? (a) - 'A' \ | ||
1690 | : (((a) >= 'a') && ((a) <= 'z')) \ | ||
1691 | ? (a) - 'a' + 26 \ | ||
1692 | : (((a) >= '0') && ((a) <= '9')) \ | ||
1693 | ? (a) - '0' + 52 \ | ||
1694 | : ((a) == '+') ? 62 : ((a) == '/') ? 63 : -1) | ||
1695 | |||
1696 | |||
1697 | #define CHECK_CRLF \ | ||
1698 | while ( (data[i] == '\r') || (data[i] == '\n') ) \ | ||
1699 | { \ | ||
1700 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, \ | ||
1701 | "ignoring CR/LF\n"); \ | ||
1702 | i++; \ | ||
1703 | if (i >= len) { \ | ||
1704 | goto END; \ | ||
1705 | } \ | ||
1706 | } | ||
1707 | |||
1708 | |||
1709 | size_t | ||
1710 | GNUNET_STRINGS_base64_decode (const char *data, | ||
1711 | size_t len, | ||
1712 | void **out) | ||
1713 | { | ||
1714 | unsigned char *output; | ||
1715 | size_t ret = 0; | ||
1716 | |||
1717 | GNUNET_assert (len / 3 < SIZE_MAX); | ||
1718 | output = GNUNET_malloc ((len * 3 / 4) + 8); | ||
1719 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1720 | "base64_decode decoding len=%d\n", | ||
1721 | (int) len); | ||
1722 | for (size_t i = 0; i < len; ++i) | ||
1723 | { | ||
1724 | unsigned char c; | ||
1725 | unsigned char c1; | ||
1726 | |||
1727 | CHECK_CRLF; | ||
1728 | if (FILLCHAR == data[i]) | ||
1729 | break; | ||
1730 | c = (unsigned char) cvtfind (data[i]); | ||
1731 | ++i; | ||
1732 | CHECK_CRLF; | ||
1733 | c1 = (unsigned char) cvtfind (data[i]); | ||
1734 | c = (c << 2) | ((c1 >> 4) & 0x3); | ||
1735 | output[ret++] = c; | ||
1736 | if (++i < len) | ||
1737 | { | ||
1738 | CHECK_CRLF; | ||
1739 | c = data[i]; | ||
1740 | if (FILLCHAR == c) | ||
1741 | break; | ||
1742 | c = (unsigned char) cvtfind (c); | ||
1743 | c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf); | ||
1744 | output[ret++] = c1; | ||
1745 | } | ||
1746 | if (++i < len) | ||
1747 | { | ||
1748 | CHECK_CRLF; | ||
1749 | c1 = data[i]; | ||
1750 | if (FILLCHAR == c1) | ||
1751 | break; | ||
1752 | |||
1753 | c1 = (unsigned char) cvtfind (c1); | ||
1754 | c = ((c << 6) & 0xc0) | c1; | ||
1755 | output[ret++] = c; | ||
1756 | } | ||
1757 | } | ||
1758 | END: | ||
1759 | *out = output; | ||
1760 | return ret; | ||
1761 | } | ||
1762 | |||
1763 | |||
1764 | #undef CHECK_CRLF | ||
1765 | |||
1766 | |||
1767 | size_t | ||
1768 | GNUNET_STRINGS_base64url_decode (const char *data, | ||
1769 | size_t len, | ||
1770 | void **out) | ||
1771 | { | ||
1772 | char *s; | ||
1773 | int padding; | ||
1774 | size_t ret; | ||
1775 | |||
1776 | /* make enough space for padding */ | ||
1777 | GNUNET_assert (len < SIZE_MAX - 3); | ||
1778 | s = GNUNET_malloc (len + 3); | ||
1779 | memcpy (s, | ||
1780 | data, | ||
1781 | len); | ||
1782 | for (size_t i = 0; i < strlen (s); i++) | ||
1783 | { | ||
1784 | if (s[i] == '-') | ||
1785 | s[i] = '+'; | ||
1786 | if (s[i] == '_') | ||
1787 | s[i] = '/'; | ||
1788 | } | ||
1789 | padding = len % 4; | ||
1790 | switch (padding) // Pad with trailing '='s | ||
1791 | { | ||
1792 | case 0: | ||
1793 | break; // No pad chars in this case | ||
1794 | case 2: | ||
1795 | memcpy (&s[len], | ||
1796 | "==", | ||
1797 | 2); | ||
1798 | len += 2; | ||
1799 | break; // Two pad chars | ||
1800 | case 3: | ||
1801 | s[len] = '='; | ||
1802 | len++; | ||
1803 | break; // One pad char | ||
1804 | default: | ||
1805 | GNUNET_assert (0); | ||
1806 | break; | ||
1807 | } | ||
1808 | ret = GNUNET_STRINGS_base64_decode (s, | ||
1809 | len, | ||
1810 | out); | ||
1811 | GNUNET_free (s); | ||
1812 | return ret; | ||
1813 | } | ||
1814 | |||
1815 | |||
1816 | size_t | ||
1817 | GNUNET_STRINGS_urldecode (const char *data, | ||
1818 | size_t len, | ||
1819 | char **out) | ||
1820 | { | ||
1821 | const char *rpos = data; | ||
1822 | *out = GNUNET_malloc (len + 1); /* output should always fit into input */ | ||
1823 | char *wpos = *out; | ||
1824 | size_t resl = 0; | ||
1825 | |||
1826 | while ( ('\0' != *rpos) && | ||
1827 | (data + len != rpos) ) | ||
1828 | { | ||
1829 | unsigned int num; | ||
1830 | switch (*rpos) | ||
1831 | { | ||
1832 | case '%': | ||
1833 | if (rpos + 3 > data + len) | ||
1834 | { | ||
1835 | GNUNET_break_op (0); | ||
1836 | GNUNET_free (*out); | ||
1837 | return 0; | ||
1838 | } | ||
1839 | if (1 != sscanf (rpos + 1, | ||
1840 | "%2x", | ||
1841 | &num)) | ||
1842 | break; | ||
1843 | *wpos = (char) ((unsigned char) num); | ||
1844 | wpos++; | ||
1845 | resl++; | ||
1846 | rpos += 3; | ||
1847 | break; | ||
1848 | /* TODO: add bad sequence handling */ | ||
1849 | /* intentional fall through! */ | ||
1850 | default: | ||
1851 | *wpos = *rpos; | ||
1852 | wpos++; | ||
1853 | resl++; | ||
1854 | rpos++; | ||
1855 | } | ||
1856 | } | ||
1857 | *wpos = '\0'; /* add 0-terminator */ | ||
1858 | return resl; | ||
1859 | } | ||
1860 | |||
1861 | |||
1862 | size_t | ||
1863 | GNUNET_STRINGS_urlencode (const char *data, | ||
1864 | size_t len, | ||
1865 | char **out) | ||
1866 | { | ||
1867 | struct GNUNET_Buffer buf = { 0 }; | ||
1868 | const uint8_t *i8 = (uint8_t *) data; | ||
1869 | |||
1870 | while (0 != *i8) | ||
1871 | { | ||
1872 | if (0 == (0x80 & *i8)) | ||
1873 | { | ||
1874 | /* traditional ASCII */ | ||
1875 | if (isalnum (*i8) || | ||
1876 | (*i8 == '-') || | ||
1877 | (*i8 == '_') || | ||
1878 | (*i8 == '.') || | ||
1879 | (*i8 == '~') ) | ||
1880 | GNUNET_buffer_write (&buf, | ||
1881 | (const char*) i8, | ||
1882 | 1); | ||
1883 | else if (*i8 == ' ') | ||
1884 | GNUNET_buffer_write (&buf, | ||
1885 | "+", | ||
1886 | 1); | ||
1887 | else | ||
1888 | GNUNET_buffer_write_fstr (&buf, | ||
1889 | "%%%X%X", | ||
1890 | *i8 >> 4, | ||
1891 | *i8 & 15); | ||
1892 | i8++; | ||
1893 | continue; | ||
1894 | } | ||
1895 | if (0x80 + 0x40 == ((0x80 + 0x40 + 0x20) & *i8)) | ||
1896 | { | ||
1897 | /* 2-byte value, percent-encode */ | ||
1898 | GNUNET_buffer_write_fstr (&buf, | ||
1899 | "%%%X%X", | ||
1900 | *i8 >> 4, | ||
1901 | *i8 & 15); | ||
1902 | i8++; | ||
1903 | GNUNET_buffer_write_fstr (&buf, | ||
1904 | "%%%X%X", | ||
1905 | *i8 >> 4, | ||
1906 | *i8 & 15); | ||
1907 | i8++; | ||
1908 | continue; | ||
1909 | } | ||
1910 | if (0x80 + 0x40 + 0x20 == ((0x80 + 0x40 + 0x20 + 0x10) & *i8)) | ||
1911 | { | ||
1912 | /* 3-byte value, percent-encode */ | ||
1913 | for (unsigned int i = 0; i<3; 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 == ((0x80 + 0x40 + 0x20 + 0x10 + 0x08) & *i8)) | ||
1924 | { | ||
1925 | /* 4-byte value, percent-encode */ | ||
1926 | for (unsigned int i = 0; i<4; i++) | ||
1927 | { | ||
1928 | GNUNET_buffer_write_fstr (&buf, | ||
1929 | "%%%X%X", | ||
1930 | *i8 >> 4, | ||
1931 | *i8 & 15); | ||
1932 | i8++; | ||
1933 | } | ||
1934 | continue; | ||
1935 | } | ||
1936 | if (0x80 + 0x40 + 0x20 + 0x10 + 0x08 == ((0x80 + 0x40 + 0x20 + 0x10 + 0x08 | ||
1937 | + 0x04) & *i8)) | ||
1938 | { | ||
1939 | /* 5-byte value, percent-encode (outside of UTF-8 modern standard, but so what) */ | ||
1940 | for (unsigned int i = 0; i<5; i++) | ||
1941 | { | ||
1942 | GNUNET_buffer_write_fstr (&buf, | ||
1943 | "%%%X%X", | ||
1944 | *i8 >> 4, | ||
1945 | *i8 & 15); | ||
1946 | i8++; | ||
1947 | } | ||
1948 | continue; | ||
1949 | } | ||
1950 | if (0x80 + 0x40 + 0x20 + 0x10 + 0x08 + 0x04 == ((0x80 + 0x40 + 0x20 + 0x10 | ||
1951 | + 0x08 + 0x04 + 0x02) | ||
1952 | & *i8)) | ||
1953 | { | ||
1954 | /* 6-byte value, percent-encode (outside of UTF-8 modern standard, but so what) */ | ||
1955 | for (unsigned int i = 0; i<6; i++) | ||
1956 | { | ||
1957 | GNUNET_buffer_write_fstr (&buf, | ||
1958 | "%%%X%X", | ||
1959 | *i8 >> 4, | ||
1960 | *i8 & 15); | ||
1961 | i8++; | ||
1962 | } | ||
1963 | continue; | ||
1964 | } | ||
1965 | /* really, really invalid UTF-8: fail */ | ||
1966 | GNUNET_break (0); | ||
1967 | GNUNET_buffer_clear (&buf); | ||
1968 | return 0; | ||
1969 | } | ||
1970 | *out = GNUNET_buffer_reap_str (&buf); | ||
1971 | return strlen (*out); | ||
1972 | } | ||
1973 | |||
1974 | |||
1975 | /** | ||
1976 | * Sometimes we use the binary name to determine which specific | ||
1977 | * test to run. In those cases, the string after the last "_" | ||
1978 | * in 'argv[0]' specifies a string that determines the configuration | ||
1979 | * file or plugin to use. | ||
1980 | * | ||
1981 | * This function returns the respective substring, taking care | ||
1982 | * of issues such as binaries ending in '.exe' on W32. | ||
1983 | * | ||
1984 | * @param argv0 the name of the binary | ||
1985 | * @return string between the last '_' and the '.exe' (or the end of the string), | ||
1986 | * NULL if argv0 has no '_' | ||
1987 | */ | ||
1988 | char * | ||
1989 | GNUNET_STRINGS_get_suffix_from_binary_name (const char *argv0) | ||
1990 | { | ||
1991 | const char *ret; | ||
1992 | const char *dot; | ||
1993 | |||
1994 | ret = strrchr (argv0, '_'); | ||
1995 | if (NULL == ret) | ||
1996 | return NULL; | ||
1997 | ret++; /* skip underscore */ | ||
1998 | dot = strchr (ret, | ||
1999 | '.'); | ||
2000 | if (NULL != dot) | ||
2001 | return GNUNET_strndup (ret, | ||
2002 | dot - ret); | ||
2003 | return GNUNET_strdup (ret); | ||
2004 | } | ||
2005 | |||
2006 | |||
2007 | /* end of strings.c */ | ||