1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2002-2011 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2012 Planets Communications B.V.
6 Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22 */
23 /*
24 * edit.c edit string to ascii, and ascii to internal
25 *
26 * Kern Sibbald, December MMII
27 */
28
29 #include "include/bareos.h"
30 #include "lib/edit.h"
31 #include <math.h>
32
33 #define DEFAULT_FORMAT_LENGTH 27
34
35 /*
36 * We assume ASCII input and don't worry about overflow
37 */
str_to_uint64(const char * str)38 uint64_t str_to_uint64(const char* str)
39 {
40 const char* p = str;
41 uint64_t value = 0;
42
43 if (!p) { return 0; }
44
45 while (B_ISSPACE(*p)) { p++; }
46
47 if (*p == '+') { p++; }
48
49 while (B_ISDIGIT(*p)) {
50 value = B_TIMES10(value) + *p - '0';
51 p++;
52 }
53
54 return value;
55 }
56
str_to_int64(const char * str)57 int64_t str_to_int64(const char* str)
58 {
59 const char* p = str;
60 int64_t value;
61 bool negative = false;
62
63 if (!p) { return 0; }
64
65 while (B_ISSPACE(*p)) { p++; }
66
67 if (*p == '+') {
68 p++;
69 } else if (*p == '-') {
70 negative = true;
71 p++;
72 }
73
74 value = str_to_uint64(p);
75 if (negative) { value = -value; }
76
77 return value;
78 }
79
80 /*
81 * Edit an integer number with commas, the supplied buffer must be at least
82 * DEFAULT_FORMAT_LENGTH bytes long. The incoming number is always widened to 64
83 * bits.
84 */
edit_uint64_with_commas(uint64_t val,char * buf)85 char* edit_uint64_with_commas(uint64_t val, char* buf)
86 {
87 edit_uint64(val, buf);
88
89 return add_commas(buf, buf);
90 }
91
92 /*
93 * Edit an integer into "human-readable" format with four or fewer significant
94 * digits followed by a suffix that indicates the scale factor. The buf array
95 * inherits a DEFAULT_FORMAT_LENGTH byte minimum length requirement from
96 * edit_unit64_with_commas(), although the output string is limited to eight
97 * characters.
98 */
edit_uint64_with_suffix(uint64_t val,char * buf)99 char* edit_uint64_with_suffix(uint64_t val, char* buf)
100 {
101 int commas = 0;
102 char *c, mbuf[50];
103 static const char* suffix[]
104 = {"", "K", "M", "G", "T", "P", "E", "Z", "Y", "FIX ME"};
105 int suffixes = sizeof(suffix) / sizeof(*suffix);
106
107 edit_uint64_with_commas(val, mbuf);
108 if ((c = strchr(mbuf, ',')) != NULL) {
109 commas++;
110 *c++ = '.';
111 while ((c = strchr(c, ',')) != NULL) {
112 commas++;
113 *c++ = '\0';
114 }
115 mbuf[5] = '\0'; /* Drop this to get '123.456 TB' rather than '123.4 TB' */
116 }
117
118 if (commas >= suffixes) { commas = suffixes - 1; }
119 Bsnprintf(buf, DEFAULT_FORMAT_LENGTH, "%s %s", mbuf, suffix[commas]);
120
121 return buf;
122 }
123
124 /*
125 * Edit an integer number, the supplied buffer must be at least
126 * DEFAULT_FORMAT_LENGTH bytes long. The incoming number is always widened to 64
127 * bits. Replacement for sprintf(buf, "%" llu, val)
128 */
edit_uint64(uint64_t val,char * buf)129 char* edit_uint64(uint64_t val, char* buf)
130 {
131 char mbuf[50];
132 mbuf[sizeof(mbuf) - 1] = 0;
133 int i = sizeof(mbuf) - 2; /* Edit backward */
134
135 if (val == 0) {
136 mbuf[i--] = '0';
137 } else {
138 while (val != 0) {
139 mbuf[i--] = "0123456789"[val % 10];
140 val /= 10;
141 }
142 }
143 bstrncpy(buf, &mbuf[i + 1], DEFAULT_FORMAT_LENGTH);
144
145 return buf;
146 }
147
148 /*
149 * Edit an integer number, the supplied buffer must be at least
150 * DEFAULT_FORMAT_LENGTH bytes long. The incoming number is always widened to 64
151 * bits. Replacement for sprintf(buf, "%" llu, val)
152 */
edit_int64(int64_t val,char * buf)153 char* edit_int64(int64_t val, char* buf)
154 {
155 char mbuf[50];
156 bool negative = false;
157 mbuf[sizeof(mbuf) - 1] = 0;
158 int i = sizeof(mbuf) - 2; /* Edit backward */
159
160 if (val == 0) {
161 mbuf[i--] = '0';
162 } else {
163 if (val < 0) {
164 negative = true;
165 val = -val;
166 }
167 while (val != 0) {
168 mbuf[i--] = "0123456789"[val % 10];
169 val /= 10;
170 }
171 }
172 if (negative) { mbuf[i--] = '-'; }
173 bstrncpy(buf, &mbuf[i + 1], DEFAULT_FORMAT_LENGTH);
174
175 return buf;
176 }
177
178 /*
179 * Edit an integer number with commas, the supplied buffer must be at least
180 * DEFAULT_FORMAT_LENGTH bytes long. The incoming number is always widened to 64
181 * bits.
182 */
edit_int64_with_commas(int64_t val,char * buf)183 char* edit_int64_with_commas(int64_t val, char* buf)
184 {
185 edit_int64(val, buf);
186
187 return add_commas(buf, buf);
188 }
189
190 /*
191 * Given a string "str", separate the numeric part into str, and the modifier
192 * into mod.
193 */
GetModifier(char * str,char * num,int num_len,char * mod,int mod_len)194 static bool GetModifier(char* str,
195 char* num,
196 int num_len,
197 char* mod,
198 int mod_len)
199 {
200 int i, len, num_begin, num_end, mod_begin, mod_end;
201
202 StripTrailingJunk(str);
203 len = strlen(str);
204
205 for (i = 0; i < len; i++) {
206 if (!B_ISSPACE(str[i])) { break; }
207 }
208 num_begin = i;
209
210 /*
211 * Walk through integer part
212 */
213 for (; i < len; i++) {
214 if (!B_ISDIGIT(str[i]) && str[i] != '.') { break; }
215 }
216
217 num_end = i;
218 if (num_len > (num_end - num_begin + 1)) {
219 num_len = num_end - num_begin + 1;
220 }
221 if (num_len == 0) { return false; }
222
223 /*
224 * Eat any spaces in front of modifier
225 */
226 for (; i < len; i++) {
227 if (!B_ISSPACE(str[i])) { break; }
228 }
229
230 mod_begin = i;
231 for (; i < len; i++) {
232 if (!B_ISALPHA(str[i])) { break; }
233 }
234
235 mod_end = i;
236 if (mod_len > (mod_end - mod_begin + 1)) {
237 mod_len = mod_end - mod_begin + 1;
238 }
239
240 Dmsg5(900, "str=%s: num_beg=%d num_end=%d mod_beg=%d mod_end=%d\n", str,
241 num_begin, num_end, mod_begin, mod_end);
242 bstrncpy(num, &str[num_begin], num_len);
243 bstrncpy(mod, &str[mod_begin], mod_len);
244
245 if (!Is_a_number(num)) { return false; }
246
247 bstrncpy(str, &str[mod_end], len);
248 Dmsg2(900, "num=%s mod=%s\n", num, mod);
249
250 return true;
251 }
252
253 /*
254 * Convert a string duration to utime_t (64 bit seconds)
255 * Returns false: if error
256 * true: if OK, and value stored in value
257 */
DurationToUtime(char * str,utime_t * value)258 bool DurationToUtime(char* str, utime_t* value)
259 {
260 int i, mod_len;
261 double val, total = 0.0;
262 char mod_str[20];
263 char num_str[50];
264 /*
265 * The "n" = mins and months appears before minutes so that m maps to months.
266 */
267 static const char* mod[]
268 = {"n", "seconds", "months", "minutes", "mins", "hours",
269 "days", "weeks", "quarters", "years", (char*)NULL};
270 static const int32_t mult[] = {60,
271 1,
272 60 * 60 * 24 * 30,
273 60,
274 60,
275 3600,
276 3600 * 24,
277 3600 * 24 * 7,
278 3600 * 24 * 91,
279 3600 * 24 * 365,
280 0};
281
282 while (*str) {
283 if (!GetModifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
284 return false;
285 }
286
287 /*
288 * Now find the multiplier corresponding to the modifier
289 */
290 mod_len = strlen(mod_str);
291 if (mod_len == 0) {
292 i = 1; /* Default to seconds */
293 } else {
294 for (i = 0; mod[i]; i++) {
295 if (bstrncasecmp(mod_str, mod[i], mod_len)) { break; }
296 }
297 if (mod[i] == NULL) { return false; }
298 }
299
300 Dmsg2(900, "str=%s: mult=%d\n", num_str, mult[i]);
301 errno = 0;
302 val = strtod(num_str, NULL);
303
304 if (errno != 0 || val < 0) { return false; }
305
306 total += val * mult[i];
307 }
308 *value = (utime_t)total;
309
310 return true;
311 }
312
313 /*
314 * Edit a utime "duration" into ASCII
315 */
edit_utime(utime_t val,char * buf,int buf_len)316 char* edit_utime(utime_t val, char* buf, int buf_len)
317 {
318 char mybuf[200];
319 static const int32_t mult[]
320 = {60 * 60 * 24 * 365, 60 * 60 * 24 * 30, 60 * 60 * 24, 60 * 60, 60};
321 static const char* mod[] = {"year", "month", "day", "hour", "min"};
322 int i;
323 uint32_t times;
324
325 *buf = 0;
326 for (i = 0; i < 5; i++) {
327 times = (uint32_t)(val / mult[i]);
328 if (times > 0) {
329 val = val - (utime_t)times * mult[i];
330 Bsnprintf(mybuf, sizeof(mybuf), "%d %s%s ", times, mod[i],
331 times > 1 ? "s" : "");
332 bstrncat(buf, mybuf, buf_len);
333 }
334 }
335
336 if (val == 0 && strlen(buf) == 0) {
337 bstrncat(buf, "0 secs", buf_len);
338 } else if (val != 0) {
339 Bsnprintf(mybuf, sizeof(mybuf), "%d sec%s", (uint32_t)val,
340 val > 1 ? "s" : "");
341 bstrncat(buf, mybuf, buf_len);
342 }
343
344 return buf;
345 }
346
edit_pthread(pthread_t val,char * buf,int buf_len)347 char* edit_pthread(pthread_t val, char* buf, int buf_len)
348 {
349 int i;
350 char mybuf[3];
351 unsigned char* ptc = (unsigned char*)(void*)(&val);
352
353 bstrncpy(buf, "0x", buf_len);
354 for (i = sizeof(val); i; --i) {
355 Bsnprintf(mybuf, sizeof(mybuf), "%02x", (unsigned)(ptc[i]));
356 bstrncat(buf, mybuf, buf_len);
357 }
358
359 return buf;
360 }
361
strunit_to_uint64(char * str,uint64_t * value,const char ** mod)362 static bool strunit_to_uint64(char* str, uint64_t* value, const char** mod)
363 {
364 int i, mod_len;
365 double val;
366 char mod_str[20];
367 char num_str[50];
368 static const uint64_t mult[] = {
369 1, // Byte
370 1024, // KiB Kibibyte
371 1000, // kB Kilobyte
372
373 1048576, // MiB Mebibyte
374 1000000, // MB Megabyte
375
376 1073741824, // GiB Gibibyte
377 1000000000, // GB Gigabyte
378
379 1099511627776, // TiB Tebibyte
380 1000000000000, // TB Terabyte
381
382 1125899906842624, // PiB Pebibyte
383 1000000000000000, // PB Petabyte
384
385 1152921504606846976, // EiB Exbibyte
386 1000000000000000000, // EB Exabyte
387 // 18446744073709551616 2^64
388
389 };
390
391 if (!GetModifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
392 return 0;
393 }
394
395 /*
396 * Now find the multiplier corresponding to the modifier
397 */
398 mod_len = strlen(mod_str);
399 if (mod_len == 0) {
400 i = 0; /* Default with no modifier = 1 */
401 } else {
402 for (i = 0; mod[i]; i++) {
403 if (bstrncasecmp(mod_str, mod[i], mod_len)) { break; }
404 }
405 if (mod[i] == NULL) { return false; }
406 }
407
408 Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
409 errno = 0;
410 val = strtod(num_str, NULL);
411
412 if (errno != 0 || val < 0) { return false; }
413 *value = (utime_t)(val * mult[i]);
414
415 return true;
416 }
417
418 // convert uint64 number to size string
SizeAsSiPrefixFormat(uint64_t value_in)419 std::string SizeAsSiPrefixFormat(uint64_t value_in)
420 {
421 uint64_t value = value_in;
422 int factor;
423 std::string result{};
424 /*
425 * convert default value string to numeric value
426 */
427 static const char* modifier[]
428 = {" e", " p", " t", " g", " m", " k", "", NULL};
429 const uint64_t multiplier[] = {1152921504606846976, // EiB Exbibyte
430 1125899906842624, // PiB Pebibyte
431 1099511627776, // TiB Tebibyte
432 1073741824, // GiB Gibibyte
433 1048576, // MiB Mebibyte
434 1024, // KiB Kibibyte
435 1};
436
437 if (value == 0) {
438 result += "0";
439 } else {
440 for (int t = 0; modifier[t] && (value > 0); t++) {
441 factor = value / multiplier[t];
442 value = value % multiplier[t];
443 if (factor > 0) {
444 result += std::to_string(factor);
445 result += modifier[t];
446 if (value > 0) { result += " "; }
447 }
448 }
449 }
450 return result;
451 }
452
453
454 /*
455 * Convert a size in bytes to uint64_t
456 * Returns false: if error
457 * true: if OK, and value stored in value
458 */
size_to_uint64(char * str,uint64_t * value)459 bool size_to_uint64(char* str, uint64_t* value)
460 {
461 /*
462 * First item * not used
463 */
464 // clang-format off
465 static const char* mod[] = {"*",
466 // kibibyte, kilobyte
467 "k", "kb",
468 // mebibyte, megabyte
469 "m", "mb",
470 // gibibyte, gigabyte
471 "g", "gb",
472 // tebibyte, terabyte
473 "t", "tb",
474 // pebibyte, petabyte
475 "p", "pb",
476 // exbibyte, exabyte
477 "e", "eb",
478 NULL};
479
480 // clang-format on
481 return strunit_to_uint64(str, value, mod);
482 }
483
484 /*
485 * Convert a speed in bytes/s to uint64_t
486 * Returns false: if error
487 * true: if OK, and value stored in value
488 */
speed_to_uint64(char * str,uint64_t * value)489 bool speed_to_uint64(char* str, uint64_t* value)
490 {
491 /*
492 * First item * not used
493 */
494 static const char* mod[] = {"*", "k/s", "kb/s", "m/s", "mb/s", NULL};
495
496 return strunit_to_uint64(str, value, mod);
497 }
498
499 /*
500 * Check if specified string is a number or not.
501 * Taken from SQLite, cool, thanks.
502 */
Is_a_number(const char * n)503 bool Is_a_number(const char* n)
504 {
505 bool digit_seen = false;
506
507 if (*n == '-' || *n == '+') { n++; }
508
509 while (B_ISDIGIT(*n)) {
510 digit_seen = true;
511 n++;
512 }
513
514 if (digit_seen && *n == '.') {
515 n++;
516 while (B_ISDIGIT(*n)) { n++; }
517 }
518
519 if (digit_seen && (*n == 'e' || *n == 'E')
520 && (B_ISDIGIT(n[1])
521 || ((n[1] == '-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
522 n += 2; /* Skip e- or e+ or e digit */
523 while (B_ISDIGIT(*n)) { n++; }
524 }
525
526 return digit_seen && *n == 0;
527 }
528
529 /*
530 * Check if specified string is a list of numbers or not
531 */
Is_a_number_list(const char * n)532 bool Is_a_number_list(const char* n)
533 {
534 bool previous_digit = false;
535 bool digit_seen = false;
536
537 while (*n) {
538 if (B_ISDIGIT(*n)) {
539 previous_digit = true;
540 digit_seen = true;
541 } else if (*n == ',' && previous_digit) {
542 previous_digit = false;
543 } else {
544 return false;
545 }
546 n++;
547 }
548
549 return digit_seen && *n == 0;
550 }
551
552 /*
553 * Check if the specified string is an integer
554 */
IsAnInteger(const char * n)555 bool IsAnInteger(const char* n)
556 {
557 bool digit_seen = false;
558 while (B_ISDIGIT(*n)) {
559 digit_seen = true;
560 n++;
561 }
562 return digit_seen && *n == 0;
563 }
564
565 /*
566 * Check if BAREOS Resource Name is valid
567 *
568 * If ua is non-NULL send the message
569 */
IsNameValid(const char * name,POOLMEM * & msg)570 bool IsNameValid(const char* name, POOLMEM*& msg)
571 {
572 int len;
573 const char* p;
574 /*
575 * Special characters to accept
576 */
577 const char* accept = ":.-_/ ";
578
579 /* Empty name is invalid */
580 if (!name) {
581 if (msg) { Mmsg(msg, _("Empty name not allowed.\n")); }
582 return false;
583 }
584
585 /* check for beginning space */
586 if (name[0] == ' ') {
587 if (msg) { Mmsg(msg, _("Name cannot start with space.\n")); }
588 return false;
589 }
590
591 /*
592 * Restrict the characters permitted in the Volume name
593 */
594 for (p = name; *p; p++) {
595 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
596 continue;
597 }
598 if (msg) { Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p); }
599 return false;
600 }
601
602 len = p - name;
603 if (len >= MAX_NAME_LENGTH) {
604 if (msg) { Mmsg(msg, _("Name too long.\n")); }
605 return false;
606 }
607
608 if (len == 0) {
609 if (msg) { Mmsg(msg, _("Name must be at least one character long.\n")); }
610 return false;
611 } else {
612 /* check for ending space */
613 if (*(p - 1) == ' ') {
614 if (msg) { Mmsg(msg, _("Name cannot end with space.\n")); }
615 return false;
616 }
617 }
618
619 return true;
620 }
621
IsNameValid(const char * name)622 bool IsNameValid(const char* name)
623 {
624 bool retval;
625 POOLMEM* msg = GetPoolMemory(PM_NAME);
626
627 retval = IsNameValid(name, msg);
628
629 FreePoolMemory(msg);
630
631 return retval;
632 }
633
634 /*
635 * Add commas to a string, which is presumably a number.
636 */
add_commas(char * val,char * buf)637 char* add_commas(char* val, char* buf)
638 {
639 int len, nc;
640 char *p, *q;
641 int i;
642
643 if (val != buf) { strcpy(buf, val); }
644 len = strlen(buf);
645 if (len < 1) { len = 1; }
646 nc = (len - 1) / 3;
647 p = buf + len;
648 q = p + nc;
649 *q-- = *p--;
650 for (; nc; nc--) {
651 for (i = 0; i < 3; i++) { *q-- = *p--; }
652 *q-- = ',';
653 }
654
655 return buf;
656 }
657
658 /*
659 * check if acl entry is valid
660 * valid acl entries contain only A-Z 0-9 and !*.:_-'/
661 */
IsAclEntryValid(const char * acl,std::vector<char> & msg)662 bool IsAclEntryValid(const char* acl, std::vector<char>& msg)
663 {
664 int len;
665 const char* p;
666 const char* accept = "!()[]|+?*.:_-'/"; /* Special characters to accept */
667
668 if (!acl) {
669 Mmsg(msg, _("Empty acl not allowed.\n"));
670 return false;
671 }
672
673 /* Restrict the characters permitted in acl */
674 for (p = acl; *p; p++) {
675 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
676 continue;
677 }
678 Mmsg(msg, _("Illegal character \"%c\" in acl.\n"), *p);
679 return false;
680 }
681
682 len = p - acl;
683 if (len >= MAX_NAME_LENGTH) {
684 Mmsg(msg, _("Acl too long.\n"));
685 return false;
686 }
687
688 if (len == 0) {
689 Mmsg(msg, _("Acl must be at least one character long.\n"));
690 return false;
691 }
692
693 return true;
694 }
695
IsAclEntryValid(const char * acl)696 bool IsAclEntryValid(const char* acl)
697 {
698 std::vector<char> msg;
699 return IsAclEntryValid(acl, msg);
700 }
701