Coverage Report

Created: 2026-01-17 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/str-parse.c
Line
Count
Source
1
/* Copyright (c) 2022 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "str-parse.h"
5
6
#include <ctype.h>
7
8
static int str_parse_get_interval_full(
9
  const char *str, unsigned int *interval_r, bool milliseconds,
10
  const char **error_r)
11
0
{
12
0
  uintmax_t num, multiply = milliseconds ? 1000 : 1;
13
0
  const char *p;
14
15
0
  if (str_parse_uintmax(str, &num, &p) < 0) {
16
0
    *error_r = t_strconcat("Invalid time interval: ", str, NULL);
17
0
    return -1;
18
0
  }
19
0
  while (*p == ' ') p++;
20
0
  if (*p == '\0' && num != 0) {
21
0
    *error_r = t_strdup_printf("Time interval '%s' is missing units "
22
0
      "(add e.g. 's' for seconds)", str);
23
0
    return -1;
24
0
  }
25
0
  switch (i_toupper(*p)) {
26
0
  case 'S':
27
0
    multiply *= 1;
28
0
    if (str_begins_icase_with("secs", p) ||
29
0
        str_begins_icase_with("seconds", p))
30
0
      p = "";
31
0
    break;
32
0
  case 'M':
33
0
    multiply *= 60;
34
0
    if (str_begins_icase_with("mins", p) ||
35
0
        str_begins_icase_with("minutes", p))
36
0
      p = "";
37
0
    else if (str_begins_icase_with("msecs", p) ||
38
0
       str_begins_icase_with("mseconds", p) ||
39
0
       str_begins_icase_with("millisecs", p) ||
40
0
       str_begins_icase_with("milliseconds", p)) {
41
0
      if (milliseconds || (num % 1000) == 0) {
42
0
        if (!milliseconds) {
43
          /* allow ms also for seconds, as long
44
             as it's divisible by seconds */
45
0
          num /= 1000;
46
0
        }
47
0
        multiply = 1;
48
0
        p = "";
49
0
        break;
50
0
      }
51
0
      *error_r = t_strdup_printf(
52
0
        "Milliseconds not supported for this setting: %s", str);
53
0
      return -1;
54
0
    }
55
0
    break;
56
0
  case 'H':
57
0
    multiply *= 60*60;
58
0
    if (str_begins_icase_with("hours", p))
59
0
      p = "";
60
0
    break;
61
0
  case 'D':
62
0
    multiply *= 60*60*24;
63
0
    if (str_begins_icase_with("days", p))
64
0
      p = "";
65
0
    break;
66
0
  case 'W':
67
0
    multiply *= 60*60*24*7;
68
0
    if (str_begins_icase_with("weeks", p))
69
0
      p = "";
70
0
    break;
71
0
  }
72
73
0
  if (*p != '\0') {
74
0
    *error_r = t_strconcat("Invalid time interval: ", str, NULL);
75
0
    return -1;
76
0
  }
77
0
  if (num > UINT_MAX / multiply) {
78
0
    *error_r = t_strconcat("Time interval is too large: ",
79
0
               str, NULL);
80
0
    return -1;
81
0
  }
82
0
  *interval_r = num * multiply;
83
0
  return 0;
84
0
}
85
86
int str_parse_get_interval(const char *str, unsigned int *secs_r,
87
         const char **error_r)
88
0
{
89
0
  return str_parse_get_interval_full(str, secs_r, FALSE, error_r);
90
0
}
91
92
int str_parse_get_interval_msecs(const char *str, unsigned int *msecs_r,
93
         const char **error_r)
94
0
{
95
0
  return str_parse_get_interval_full(str, msecs_r, TRUE, error_r);
96
0
}
97
98
int str_parse_get_size(const char *str, uoff_t *bytes_r,
99
           const char **error_r)
100
0
{
101
0
  uintmax_t num, multiply = 1;
102
0
  const char *p;
103
104
0
  if (str_parse_uintmax(str, &num, &p) < 0) {
105
0
    *error_r = t_strconcat("Invalid size: ", str, NULL);
106
0
    return -1;
107
0
  }
108
0
  while (*p == ' ') p++;
109
0
  switch (i_toupper(*p)) {
110
0
  case 'B':
111
0
    multiply = 1;
112
0
    p += 1;
113
0
    break;
114
0
  case 'K':
115
0
    multiply = 1024;
116
0
    p += 1;
117
0
    break;
118
0
  case 'M':
119
0
    multiply = 1024*1024;
120
0
    p += 1;
121
0
    break;
122
0
  case 'G':
123
0
    multiply = 1024*1024*1024;
124
0
    p += 1;
125
0
    break;
126
0
  case 'T':
127
0
    multiply = 1024ULL*1024*1024*1024;
128
0
    p += 1;
129
0
    break;
130
0
  }
131
132
0
  if (multiply > 1) {
133
    /* Allow: k, ki, kiB */
134
0
    if (i_toupper(*p) == 'I')
135
0
      p++;
136
0
    if (i_toupper(*p) == 'B')
137
0
      p++;
138
0
  }
139
0
  if (*p != '\0') {
140
0
    *error_r = t_strconcat("Invalid size: ", str, NULL);
141
0
    return -1;
142
0
  }
143
0
  if (num > (UOFF_T_MAX) / multiply) {
144
0
    *error_r = t_strconcat("Size is too large: ", str, NULL);
145
0
    return -1;
146
0
  }
147
0
  *bytes_r = num * multiply;
148
0
  return 0;
149
0
}
150
151
int str_parse_get_bool(const char *value, bool *result_r,
152
           const char **error_r)
153
0
{
154
  /* FIXME: eventually we'd want to support only yes/no */
155
0
  if (strcasecmp(value, "yes") == 0 ||
156
0
      strcasecmp(value, "y") == 0 || strcmp(value, "1") == 0)
157
0
    *result_r = TRUE;
158
0
  else if (strcasecmp(value, "no") == 0)
159
0
    *result_r = FALSE;
160
0
  else {
161
0
    *error_r = t_strdup_printf("Invalid boolean value: %s (use yes or no)",
162
0
             value);
163
0
    return -1;
164
0
  }
165
166
0
  return 0;
167
0
}