Version bump to 5.9.4
[strongswan.git] / src / libstrongswan / collections / enumerator.c
1 /*
2 * Copyright (C) 2008-2017 Tobias Brunner
3 * Copyright (C) 2007 Martin Willi
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "enumerator.h"
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <dirent.h>
25 #include <errno.h>
26 #include <string.h>
27
28 #ifdef HAVE_GLOB_H
29 #include <glob.h>
30 #elif defined(WIN32)
31 #include <fileapi.h>
32 #endif /* HAVE_GLOB_H */
33
34 #include <utils/debug.h>
35
36 /*
37 * Described in header.
38 */
39 bool enumerator_enumerate_default(enumerator_t *enumerator, ...)
40 {
41 va_list args;
42 bool result;
43
44 if (!enumerator->venumerate)
45 {
46 DBG1(DBG_LIB, "!!! ENUMERATE DEFAULT: venumerate() missing !!!");
47 return FALSE;
48 }
49 va_start(args, enumerator);
50 result = enumerator->venumerate(enumerator, args);
51 va_end(args);
52 return result;
53 }
54
55 METHOD(enumerator_t, enumerate_empty, bool,
56 enumerator_t *enumerator, va_list args)
57 {
58 return FALSE;
59 }
60
61 /*
62 * Described in header
63 */
64 enumerator_t* enumerator_create_empty()
65 {
66 enumerator_t *this;
67
68 INIT(this,
69 .enumerate = enumerator_enumerate_default,
70 .venumerate = _enumerate_empty,
71 .destroy = (void*)free,
72 );
73 return this;
74 }
75
76 /**
77 * Enumerator implementation for directory enumerator
78 */
79 typedef struct {
80 /** implements enumerator_t */
81 enumerator_t public;
82 /** directory handle */
83 DIR *dir;
84 /** absolute path of current file */
85 char full[PATH_MAX];
86 /** where directory part of full ends and relative file gets written */
87 char *full_end;
88 } dir_enum_t;
89
90 METHOD(enumerator_t, destroy_dir_enum, void,
91 dir_enum_t *this)
92 {
93 closedir(this->dir);
94 free(this);
95 }
96
97 METHOD(enumerator_t, enumerate_dir_enum, bool,
98 dir_enum_t *this, va_list args)
99 {
100 struct dirent *entry = readdir(this->dir);
101 struct stat *st;
102 size_t remaining;
103 char **relative, **absolute;
104 int len;
105
106 VA_ARGS_VGET(args, relative, absolute, st);
107
108 if (!entry)
109 {
110 return FALSE;
111 }
112 if (streq(entry->d_name, ".") || streq(entry->d_name, ".."))
113 {
114 return this->public.enumerate(&this->public, relative, absolute, st);
115 }
116 if (relative)
117 {
118 *relative = entry->d_name;
119 }
120 if (absolute || st)
121 {
122 remaining = sizeof(this->full) - (this->full_end - this->full);
123 len = snprintf(this->full_end, remaining, "%s", entry->d_name);
124 if (len < 0 || len >= remaining)
125 {
126 DBG1(DBG_LIB, "buffer too small to enumerate file '%s'",
127 entry->d_name);
128 return FALSE;
129 }
130 if (absolute)
131 {
132 *absolute = this->full;
133 }
134 if (st && stat(this->full, st))
135 {
136 /* try lstat() e.g. if a symlink is not valid anymore */
137 if ((errno != ENOENT && errno != ENOTDIR) || lstat(this->full, st))
138 {
139 DBG1(DBG_LIB, "stat() on '%s' failed: %s", this->full,
140 strerror(errno));
141 return FALSE;
142 }
143 }
144 }
145 return TRUE;
146 }
147
148 /*
149 * Described in header
150 */
151 enumerator_t* enumerator_create_directory(const char *path)
152 {
153 dir_enum_t *this;
154 int len;
155
156 INIT(this,
157 .public = {
158 .enumerate = enumerator_enumerate_default,
159 .venumerate = _enumerate_dir_enum,
160 .destroy = _destroy_dir_enum,
161 },
162 );
163
164 if (*path == '\0')
165 {
166 path = "./";
167 }
168 len = snprintf(this->full, sizeof(this->full)-1, "%s", path);
169 if (len < 0 || len >= sizeof(this->full)-1)
170 {
171 DBG1(DBG_LIB, "path string '%s' too long", path);
172 free(this);
173 return NULL;
174 }
175 /* append a '/' if not already done */
176 if (!path_is_separator(this->full[len-1]))
177 {
178 this->full[len++] = DIRECTORY_SEPARATOR[0];
179 this->full[len] = '\0';
180 }
181 this->full_end = &this->full[len];
182
183 this->dir = opendir(path);
184 if (!this->dir)
185 {
186 DBG1(DBG_LIB, "opening directory '%s' failed: %s", path,
187 strerror(errno));
188 free(this);
189 return NULL;
190 }
191 return &this->public;
192 }
193
194 #ifdef HAVE_GLOB_H
195
196 /**
197 * Enumerator implementation for glob enumerator
198 */
199 typedef struct {
200 /** implements enumerator_t */
201 enumerator_t public;
202 /** glob data */
203 glob_t glob;
204 /** iteration count */
205 u_int pos;
206 } glob_enum_t;
207
208 METHOD(enumerator_t, destroy_glob_enum, void,
209 glob_enum_t *this)
210 {
211 globfree(&this->glob);
212 free(this);
213 }
214
215 METHOD(enumerator_t, enumerate_glob_enum, bool,
216 glob_enum_t *this, va_list args)
217 {
218 struct stat *st;
219 char *match;
220 char **file;
221
222 VA_ARGS_VGET(args, file, st);
223
224 if (this->pos >= this->glob.gl_pathc)
225 {
226 return FALSE;
227 }
228 match = this->glob.gl_pathv[this->pos++];
229 if (file)
230 {
231 *file = match;
232 }
233 if (st && stat(match, st))
234 {
235 DBG1(DBG_LIB, "stat() on '%s' failed: %s", match,
236 strerror(errno));
237 return FALSE;
238 }
239 return TRUE;
240 }
241
242 /*
243 * Described in header
244 */
245 enumerator_t* enumerator_create_glob(const char *pattern)
246 {
247 glob_enum_t *this;
248 int status;
249
250 if (!pattern)
251 {
252 return enumerator_create_empty();
253 }
254
255 INIT(this,
256 .public = {
257 .enumerate = enumerator_enumerate_default,
258 .venumerate = _enumerate_glob_enum,
259 .destroy = _destroy_glob_enum,
260 },
261 );
262
263 status = glob(pattern, GLOB_ERR, NULL, &this->glob);
264 if (status == GLOB_NOMATCH)
265 {
266 DBG1(DBG_LIB, "no files found matching '%s'", pattern);
267 }
268 else if (status != 0)
269 {
270 DBG1(DBG_LIB, "expanding file pattern '%s' failed: %s", pattern,
271 strerror(errno));
272 }
273 return &this->public;
274 }
275
276 #elif defined(WIN32) /* HAVE_GLOB_H */
277
278 /**
279 * Enumerator implementation for glob enumerator on Windows
280 */
281 typedef struct {
282 /** implements enumerator_t */
283 enumerator_t public;
284 /** search handle */
285 HANDLE handle;
286 /** current file path */
287 char path[PATH_MAX];
288 /** base path */
289 char *base;
290 } glob_enum_t;
291
292 METHOD(enumerator_t, destroy_glob_enum, void,
293 glob_enum_t *this)
294 {
295 if (this->handle != INVALID_HANDLE_VALUE)
296 {
297 FindClose(this->handle);
298 }
299 free(this->base);
300 free(this);
301 }
302
303 /**
304 * Create the combined path from the given file data
305 */
306 static bool combine_glob_path(glob_enum_t *this, WIN32_FIND_DATA *data)
307 {
308 if (snprintf(this->path, sizeof(this->path), "%s%s%s", this->base,
309 DIRECTORY_SEPARATOR, data->cFileName) >= sizeof(this->path))
310 {
311 DBG1(DBG_LIB, "path for '%s' too long, ignored", data->cFileName);
312 return FALSE;
313 }
314 return TRUE;
315 }
316
317 /**
318 * Return the path and stat data for the current file
319 */
320 static bool enumerate_glob_enum_data(glob_enum_t *this, va_list args)
321 {
322 struct stat *st;
323 char **file;
324
325 VA_ARGS_VGET(args, file, st);
326
327 if (file)
328 {
329 *file = this->path;
330 }
331 if (st && stat(this->path, st))
332 {
333 DBG1(DBG_LIB, "stat() on '%s' failed: %s", this->path,
334 strerror(errno));
335 return FALSE;
336 }
337 return TRUE;
338 }
339
340 METHOD(enumerator_t, enumerate_glob_enum, bool,
341 glob_enum_t *this, va_list args)
342 {
343 WIN32_FIND_DATA data;
344
345 do
346 {
347 if (!FindNextFile(this->handle, &data))
348 {
349 return FALSE;
350 }
351 }
352 while (!combine_glob_path(this, &data));
353
354 return enumerate_glob_enum_data(this, args);
355 }
356
357 METHOD(enumerator_t, enumerate_glob_enum_first, bool,
358 glob_enum_t *this, va_list args)
359 {
360 if (enumerate_glob_enum_data(this, args))
361 {
362 this->public.venumerate = _enumerate_glob_enum;
363 return TRUE;
364 }
365 return FALSE;
366 }
367
368 /*
369 * Described in header
370 */
371 enumerator_t *enumerator_create_glob(const char *pattern)
372 {
373 glob_enum_t *this;
374 WIN32_FIND_DATA data;
375
376 if (!pattern)
377 {
378 return enumerator_create_empty();
379 }
380
381 INIT(this,
382 .public = {
383 .enumerate = enumerator_enumerate_default,
384 .venumerate = _enumerate_glob_enum_first,
385 .destroy = _destroy_glob_enum,
386 },
387 .base = path_dirname(pattern),
388 );
389
390 this->handle = FindFirstFile(pattern, &data);
391 if (this->handle == INVALID_HANDLE_VALUE)
392 {
393 if (GetLastError() == ERROR_FILE_NOT_FOUND ||
394 GetLastError() == ERROR_PATH_NOT_FOUND)
395 {
396 DBG1(DBG_LIB, "no files found matching '%s'", pattern);
397 }
398 else
399 {
400 DBG1(DBG_LIB, "FindFirstFile failed for pattern '%s' (%d)", pattern,
401 GetLastError());
402 }
403 destroy_glob_enum(this);
404 return enumerator_create_empty();
405 }
406 else if (!combine_glob_path(this, &data))
407 { /* check the next file if we can't combine the path for the first one */
408 this->public.venumerate = _enumerate_glob_enum;
409 }
410 return &this->public;
411 }
412
413 #else /* HAVE_GLOB_H */
414
415 enumerator_t* enumerator_create_glob(const char *pattern)
416 {
417 return NULL;
418 }
419
420 #endif /* HAVE_GLOB_H */
421
422 /**
423 * Enumerator implementation for token enumerator
424 */
425 typedef struct {
426 /** implements enumerator_t */
427 enumerator_t public;
428 /** string to parse */
429 char *string;
430 /** current position */
431 char *pos;
432 /** separator chars */
433 const char *sep;
434 /** trim chars */
435 const char *trim;
436 } token_enum_t;
437
438 METHOD(enumerator_t, destroy_token_enum, void,
439 token_enum_t *this)
440 {
441 free(this->string);
442 free(this);
443 }
444
445 METHOD(enumerator_t, enumerate_token_enum, bool,
446 token_enum_t *this, va_list args)
447 {
448 const char *sep, *trim;
449 char *pos = NULL, *tmp, **token;
450 bool last = FALSE;
451
452 VA_ARGS_VGET(args, token);
453
454 /* trim leading characters/separators */
455 while (*this->pos)
456 {
457 trim = this->trim;
458 while (*trim)
459 {
460 if (*trim == *this->pos)
461 {
462 this->pos++;
463 break;
464 }
465 trim++;
466 }
467 sep = this->sep;
468 while (*sep)
469 {
470 if (*sep == *this->pos)
471 {
472 this->pos++;
473 break;
474 }
475 sep++;
476 }
477 if (!*trim && !*sep)
478 {
479 break;
480 }
481 }
482
483 switch (*this->pos)
484 {
485 case '"':
486 case '\'':
487 {
488 /* read quoted token */
489 tmp = strchr(this->pos + 1, *this->pos);
490 if (tmp)
491 {
492 *token = this->pos + 1;
493 *tmp = '\0';
494 this->pos = tmp + 1;
495 return TRUE;
496 }
497 /* unterminated string, FALL-THROUGH */
498 }
499 default:
500 {
501 /* find nearest separator */
502 sep = this->sep;
503 while (*sep)
504 {
505 tmp = strchr(this->pos, *sep);
506 if (tmp && (pos == NULL || tmp < pos))
507 {
508 pos = tmp;
509 }
510 sep++;
511 }
512 *token = this->pos;
513 if (pos)
514 {
515 *pos = '\0';
516 this->pos = pos + 1;
517 }
518 else
519 {
520 last = TRUE;
521 pos = this->pos = strchr(this->pos, '\0');
522 }
523 break;
524 }
525 }
526
527 /* trim trailing characters */
528 pos--;
529 while (pos >= *token)
530 {
531 trim = this->trim;
532 while (*trim)
533 {
534 if (*trim == *pos)
535 {
536 *(pos--) = '\0';
537 break;
538 }
539 trim++;
540 }
541 if (!*trim)
542 {
543 break;
544 }
545 }
546
547 if (!last || pos >= *token)
548 {
549 return TRUE;
550 }
551 return FALSE;
552 }
553
554 /*
555 * Described in header
556 */
557 enumerator_t* enumerator_create_token(const char *string, const char *sep,
558 const char *trim)
559 {
560 token_enum_t *this;
561
562 INIT(this,
563 .public = {
564 .enumerate = enumerator_enumerate_default,
565 .venumerate = _enumerate_token_enum,
566 .destroy = _destroy_token_enum,
567 },
568 .string = strdup(string),
569 .sep = sep,
570 .trim = trim,
571 );
572 this->pos = this->string;
573
574 return &this->public;
575 }
576
577 /**
578 * Enumerator for nested enumerations
579 */
580 typedef struct {
581 enumerator_t public;
582 enumerator_t *outer;
583 enumerator_t *inner;
584 enumerator_t *(*create_inner)(void *outer, void *data);
585 void *data;
586 void (*destructor)(void *data);
587 } nested_enumerator_t;
588
589
590 METHOD(enumerator_t, enumerate_nested, bool,
591 nested_enumerator_t *this, va_list args)
592 {
593 while (TRUE)
594 {
595 while (!this->inner)
596 {
597 void *outer;
598
599 if (!this->outer->enumerate(this->outer, &outer))
600 {
601 return FALSE;
602 }
603 this->inner = this->create_inner(outer, this->data);
604 if (this->inner && !this->inner->venumerate)
605 {
606 DBG1(DBG_LIB, "!!! ENUMERATE NESTED: venumerate() missing !!!");
607 return FALSE;
608 }
609 }
610 if (this->inner->venumerate(this->inner, args))
611 {
612 return TRUE;
613 }
614 this->inner->destroy(this->inner);
615 this->inner = NULL;
616 }
617 }
618
619 METHOD(enumerator_t, destroy_nested, void,
620 nested_enumerator_t *this)
621 {
622 if (this->destructor)
623 {
624 this->destructor(this->data);
625 }
626 DESTROY_IF(this->inner);
627 this->outer->destroy(this->outer);
628 free(this);
629 }
630
631 /*
632 * Described in header
633 */
634 enumerator_t *enumerator_create_nested(enumerator_t *outer,
635 enumerator_t *(inner_constructor)(void *outer, void *data),
636 void *data, void (*destructor)(void *data))
637 {
638 nested_enumerator_t *this;
639
640 INIT(this,
641 .public = {
642 .enumerate = enumerator_enumerate_default,
643 .venumerate = _enumerate_nested,
644 .destroy = _destroy_nested,
645 },
646 .outer = outer,
647 .create_inner = inner_constructor,
648 .data = data,
649 .destructor = destructor,
650 );
651 return &this->public;
652 }
653
654 /**
655 * Enumerator for filtered enumerator
656 */
657 typedef struct {
658 enumerator_t public;
659 enumerator_t *orig;
660 void *data;
661 bool (*filter)(void*,enumerator_t*,va_list);
662 void (*destructor)(void *data);
663 } filter_enumerator_t;
664
665 METHOD(enumerator_t, destroy_filter, void,
666 filter_enumerator_t *this)
667 {
668 if (this->destructor)
669 {
670 this->destructor(this->data);
671 }
672 this->orig->destroy(this->orig);
673 free(this);
674 }
675
676 METHOD(enumerator_t, enumerate_filter, bool,
677 filter_enumerator_t *this, va_list args)
678 {
679 bool result = FALSE;
680
681 if (this->filter(this->data, this->orig, args))
682 {
683 result = TRUE;
684 }
685 return result;
686 }
687
688 /*
689 * Described in header
690 */
691 enumerator_t *enumerator_create_filter(enumerator_t *orig,
692 bool (*filter)(void *data, enumerator_t *orig, va_list args),
693 void *data, void (*destructor)(void *data))
694 {
695 filter_enumerator_t *this;
696
697 INIT(this,
698 .public = {
699 .enumerate = enumerator_enumerate_default,
700 .venumerate = _enumerate_filter,
701 .destroy = _destroy_filter,
702 },
703 .orig = orig,
704 .filter = filter,
705 .data = data,
706 .destructor = destructor,
707 );
708 return &this->public;
709 }
710
711 /**
712 * Enumerator for cleaner enumerator
713 */
714 typedef struct {
715 enumerator_t public;
716 enumerator_t *wrapped;
717 void (*cleanup)(void *data);
718 void *data;
719 } cleaner_enumerator_t;
720
721 METHOD(enumerator_t, destroy_cleaner, void,
722 cleaner_enumerator_t *this)
723 {
724 this->cleanup(this->data);
725 this->wrapped->destroy(this->wrapped);
726 free(this);
727 }
728
729 METHOD(enumerator_t, enumerate_cleaner, bool,
730 cleaner_enumerator_t *this, va_list args)
731 {
732 if (!this->wrapped->venumerate)
733 {
734 DBG1(DBG_LIB, "!!! CLEANER ENUMERATOR: venumerate() missing !!!");
735 return FALSE;
736 }
737 return this->wrapped->venumerate(this->wrapped, args);
738 }
739
740 /*
741 * Described in header
742 */
743 enumerator_t *enumerator_create_cleaner(enumerator_t *wrapped,
744 void (*cleanup)(void *data), void *data)
745 {
746 cleaner_enumerator_t *this;
747
748 INIT(this,
749 .public = {
750 .enumerate = enumerator_enumerate_default,
751 .venumerate = _enumerate_cleaner,
752 .destroy = _destroy_cleaner,
753 },
754 .wrapped = wrapped,
755 .cleanup = cleanup,
756 .data = data,
757 );
758 return &this->public;
759 }
760
761 /**
762 * Enumerator for single enumerator
763 */
764 typedef struct {
765 enumerator_t public;
766 void *item;
767 void (*cleanup)(void *item);
768 bool done;
769 } single_enumerator_t;
770
771 METHOD(enumerator_t, destroy_single, void,
772 single_enumerator_t *this)
773 {
774 if (this->cleanup)
775 {
776 this->cleanup(this->item);
777 }
778 free(this);
779 }
780
781 METHOD(enumerator_t, enumerate_single, bool,
782 single_enumerator_t *this, va_list args)
783 {
784 void **item;
785
786 VA_ARGS_VGET(args, item);
787 if (this->done)
788 {
789 return FALSE;
790 }
791 *item = this->item;
792 this->done = TRUE;
793 return TRUE;
794 }
795
796 /*
797 * Described in header
798 */
799 enumerator_t *enumerator_create_single(void *item, void (*cleanup)(void *item))
800 {
801 single_enumerator_t *this;
802
803 INIT(this,
804 .public = {
805 .enumerate = enumerator_enumerate_default,
806 .venumerate = _enumerate_single,
807 .destroy = _destroy_single,
808 },
809 .item = item,
810 .cleanup = cleanup,
811 );
812 return &this->public;
813 }