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