Coverage Report

Created: 2024-02-29 06:05

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