1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2016-2018 Bareos GmbH & Co. KG
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation and included
10    in the file LICENSE.
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
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 /*
23  * util.c  miscellaneous utility subroutines for BAREOS
24  *
25  * Kern Sibbald, MM
26  */
27 
28 #include "include/bareos.h"
29 #include "include/jcr.h"
30 #include "lib/edit.h"
31 #include "lib/ascii_control_characters.h"
32 #include "lib/bstringlist.h"
33 #include "lib/qualified_resource_name_type_converter.h"
34 #include "include/version_numbers.h"
35 
36 #include <algorithm>
37 #include <string>
38 
39 /*
40  * Various BAREOS Utility subroutines
41  */
42 
43 /*
44  * Escape special characters in bareos configuration strings
45  * needed for dumping config strings
46  */
EscapeString(PoolMem & snew,const char * old,int len)47 void EscapeString(PoolMem& snew, const char* old, int len)
48 {
49   char* n;
50   const char* o;
51 
52   snew.check_size(len * 2);
53   n = snew.c_str();
54   o = old;
55   while (len--) {
56     switch (*o) {
57       case '\'':
58         *n++ = '\'';
59         *n++ = '\'';
60         o++;
61         break;
62       case '\\':
63         *n++ = '\\';
64         *n++ = '\\';
65         o++;
66         break;
67       case 0:
68         *n++ = '\\';
69         *n++ = 0;
70         o++;
71         break;
72       case '(':
73       case ')':
74       case '<':
75       case '>':
76       case '"':
77         *n++ = '\\';
78         *n++ = *o++;
79         break;
80       default:
81         *n++ = *o++;
82         break;
83     }
84   }
85   *n = 0;
86 }
87 
88 /*
89  * Return true of buffer has all zero bytes
90  */
IsBufZero(char * buf,int len)91 bool IsBufZero(char* buf, int len)
92 {
93   uint64_t* ip;
94   char* p;
95   int i, len64, done, rem;
96 
97   if (buf[0] != 0) { return false; }
98   ip = (uint64_t*)buf;
99 
100   /*
101    * Optimize by checking uint64_t for zero
102    */
103   len64 = len / sizeof(uint64_t);
104   for (i = 0; i < len64; i++) {
105     if (ip[i] != 0) { return false; }
106   }
107   done = len64 * sizeof(uint64_t); /* bytes already checked */
108   p = buf + done;
109   rem = len - done;
110   for (i = 0; i < rem; i++) {
111     if (p[i] != 0) { return false; }
112   }
113   return true;
114 }
115 
116 
117 /*
118  * Convert a string in place to lower case
119  */
lcase(char * str)120 void lcase(char* str)
121 {
122   while (*str) {
123     if (B_ISUPPER(*str)) { *str = tolower((int)(*str)); }
124     str++;
125   }
126 }
127 
128 /*
129  * Convert spaces to non-space character.
130  * This makes scanf of fields containing spaces easier.
131  */
BashSpaces(char * str)132 void BashSpaces(char* str)
133 {
134   while (*str) {
135     if (*str == ' ') *str = 0x1;
136     str++;
137   }
138 }
139 
BashSpaces(std::string & str)140 void BashSpaces(std::string& str)
141 {
142   std::replace(str.begin(), str.end(), ' ', static_cast<char>(0x1));
143 }
144 
BashSpaces(PoolMem & pm)145 void BashSpaces(PoolMem& pm)
146 {
147   char* str = pm.c_str();
148   while (*str) {
149     if (*str == ' ') *str = 0x1;
150     str++;
151   }
152 }
153 
154 /*
155  * Convert non-space characters (0x1) back into spaces
156  */
UnbashSpaces(char * str)157 void UnbashSpaces(char* str)
158 {
159   while (*str) {
160     if (*str == 0x1) *str = ' ';
161     str++;
162   }
163 }
164 
165 /*
166  * Convert non-space characters (0x1) back into spaces
167  */
UnbashSpaces(PoolMem & pm)168 void UnbashSpaces(PoolMem& pm)
169 {
170   char* str = pm.c_str();
171   while (*str) {
172     if (*str == 0x1) *str = ' ';
173     str++;
174   }
175 }
176 
177 struct HelloInformation {
178   std::string hello_string;
179   std::string resource_type_string;
180   uint32_t position_of_name;
181   int32_t position_of_version;
182 };
183 
184 using SizeTypeOfHelloList = std::vector<std::string>::size_type;
185 
186 static std::list<HelloInformation> hello_list{
187     /* this order is important */
188     {"Hello Storage calling Start Job", "R_JOB", 5, -1},
189     {"Hello Start Storage Job", "R_JOB", 4, -1},
190     {"Hello Start Job", "R_JOB", 3, -1},
191     {"Hello Director", "R_DIRECTOR", 2, -1},
192     {"Hello Storage", "R_STORAGE", 2, -1},
193     {"Hello Client", "R_CLIENT", 2, -1},
194     {"Hello", "R_CONSOLE", 1, 4} /* "Hello %s calling version %s" */
195 };
196 
GetNameAndResourceTypeAndVersionFromHello(const std::string & input,std::string & name,std::string & r_type_str,BareosVersionNumber & bareos_version)197 bool GetNameAndResourceTypeAndVersionFromHello(
198     const std::string& input,
199     std::string& name,
200     std::string& r_type_str,
201     BareosVersionNumber& bareos_version)
202 {
203   std::list<HelloInformation>::const_iterator hello = hello_list.cbegin();
204 
205   bool found = false;
206   while (hello != hello_list.cend()) {
207     uint32_t size = hello->hello_string.size();
208     uint32_t input_size = input.size();
209     if (input_size >= size) {
210       if (!input.compare(0, size, hello->hello_string)) {
211         found = true;
212         break;
213       }
214     }
215     hello++;
216   }
217 
218   if (!found) {
219     Dmsg1(100, "Client information not found: %s", input.c_str());
220     return false;
221   }
222 
223   BStringList arguments_of_hello_string(input, ' '); /* split at blanks */
224 
225   bool ok = false;
226   if (arguments_of_hello_string.size() > hello->position_of_name) {
227     name = arguments_of_hello_string[hello->position_of_name];
228     std::replace(name.begin(), name.end(), (char)0x1, ' ');
229     r_type_str = hello->resource_type_string;
230     ok = true;
231   } else {
232     Dmsg0(100, "Failed to retrieve the name from hello message\n");
233   }
234 
235   if (ok) {
236     bareos_version = BareosVersionNumber::kUndefined;
237     if (hello->position_of_version >= 0) {
238       if (arguments_of_hello_string.size() >
239           static_cast<SizeTypeOfHelloList>(hello->position_of_version)) {
240         std::string version_str =
241             arguments_of_hello_string[hello->position_of_version];
242         if (!version_str.empty()) {
243           ok = false;
244           BStringList splittet_version(version_str, '.');
245           if (splittet_version.size() > 1) {
246             uint32_t v;
247             try {
248               v = std::stoul(splittet_version[0]) * 100;
249               v += std::stoul(splittet_version[1]);
250               bareos_version = static_cast<BareosVersionNumber>(v);
251               ok = true;
252             } catch (const std::exception& e) {
253               Dmsg0(100, "Could not read out any version from hello message\n");
254             }
255           }
256         }
257       }
258     }
259   }
260 
261   return ok;
262 }
263 
264 /*
265  * Parameter:
266  *   resultbuffer: one line string
267  *   mutlilinestring: multiline string (separated by "\n")
268  *   separator: separator string
269  *
270  * multilinestring should be indented according to resultbuffer.
271  *
272  * return:
273  *   resultbuffer: multilinestring will be added to resultbuffer.
274  *
275  * Example:
276  *   resultbuffer="initial string"
277  *   mutlilinestring="line1\nline2\nline3"
278  *   separator="->"
279  * Result:
280  *   initial string->line1
281  *                 ->line2
282  *                 ->line3
283  */
IndentMultilineString(PoolMem & resultbuffer,const char * multilinestring,const char * separator)284 const char* IndentMultilineString(PoolMem& resultbuffer,
285                                   const char* multilinestring,
286                                   const char* separator)
287 {
288   PoolMem multiline(multilinestring);
289   PoolMem indent(PM_MESSAGE);
290   char* p1 = multiline.c_str();
291   char* p2 = NULL;
292   bool line1 = true;
293   size_t len;
294 
295   /* create indentation string */
296   for (len = resultbuffer.strlen(); len > 0; len--) { indent.strcat(" "); }
297   indent.strcat(separator);
298 
299   resultbuffer.strcat(separator);
300 
301   while ((p2 = strchr(p1, '\n')) != NULL) {
302     *p2 = 0;
303     if (!line1) { resultbuffer.strcat(indent); }
304     resultbuffer.strcat(p1);
305     resultbuffer.strcat("\n");
306     p1 = p2 + 1;
307     line1 = false;
308   }
309 
310   if (!line1) { resultbuffer.strcat(indent); }
311   resultbuffer.strcat(p1);
312 
313   return resultbuffer.c_str();
314 }
315 
encode_time(utime_t utime,char * buf)316 char* encode_time(utime_t utime, char* buf)
317 {
318   struct tm tm;
319   int n = 0;
320   time_t time = utime;
321 
322 #if defined(HAVE_WIN32)
323   /*
324    * Avoid a seg fault in Microsoft's CRT localtime_r(),
325    * which incorrectly references a NULL returned from gmtime() if
326    * time is negative before or after the timezone adjustment.
327    */
328   struct tm* gtm;
329 
330   if ((gtm = gmtime(&time)) == NULL) { return buf; }
331 
332   if (gtm->tm_year == 1970 && gtm->tm_mon == 1 && gtm->tm_mday < 3) {
333     return buf;
334   }
335 #endif
336 
337   Blocaltime(&time, &tm);
338   n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900,
339               tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
340 
341   return buf + n;
342 }
343 
ConvertTimeoutToTimespec(timespec & timeout,int timeout_in_seconds)344 bool ConvertTimeoutToTimespec(timespec& timeout, int timeout_in_seconds)
345 {
346   struct timeval tv;
347   struct timezone tz;
348 
349   gettimeofday(&tv, &tz);
350   timeout.tv_nsec = tv.tv_usec * 1000;
351   timeout.tv_sec = tv.tv_sec + timeout_in_seconds;
352 
353   return true;
354 }
355 
356 /*
357  * Convert a JobStatus code into a human readable form
358  */
JobstatusToAscii(int JobStatus,char * msg,int maxlen)359 void JobstatusToAscii(int JobStatus, char* msg, int maxlen)
360 {
361   const char* jobstat;
362   char buf[100];
363 
364   switch (JobStatus) {
365     case JS_Created:
366       jobstat = _("Created");
367       break;
368     case JS_Running:
369       jobstat = _("Running");
370       break;
371     case JS_Blocked:
372       jobstat = _("Blocked");
373       break;
374     case JS_Terminated:
375       jobstat = _("OK");
376       break;
377     case JS_Incomplete:
378       jobstat = _("Error: incomplete job");
379       break;
380     case JS_FatalError:
381       jobstat = _("Fatal Error");
382       break;
383     case JS_ErrorTerminated:
384       jobstat = _("Error");
385       break;
386     case JS_Error:
387       jobstat = _("Non-fatal error");
388       break;
389     case JS_Warnings:
390       jobstat = _("OK -- with warnings");
391       break;
392     case JS_Canceled:
393       jobstat = _("Canceled");
394       break;
395     case JS_Differences:
396       jobstat = _("Verify differences");
397       break;
398     case JS_WaitFD:
399       jobstat = _("Waiting on FD");
400       break;
401     case JS_WaitSD:
402       jobstat = _("Wait on SD");
403       break;
404     case JS_WaitMedia:
405       jobstat = _("Wait for new Volume");
406       break;
407     case JS_WaitMount:
408       jobstat = _("Waiting for mount");
409       break;
410     case JS_WaitStoreRes:
411       jobstat = _("Waiting for Storage resource");
412       break;
413     case JS_WaitJobRes:
414       jobstat = _("Waiting for Job resource");
415       break;
416     case JS_WaitClientRes:
417       jobstat = _("Waiting for Client resource");
418       break;
419     case JS_WaitMaxJobs:
420       jobstat = _("Waiting on Max Jobs");
421       break;
422     case JS_WaitStartTime:
423       jobstat = _("Waiting for Start Time");
424       break;
425     case JS_WaitPriority:
426       jobstat = _("Waiting on Priority");
427       break;
428     case JS_DataCommitting:
429       jobstat = _("SD committing Data");
430       break;
431     case JS_DataDespooling:
432       jobstat = _("SD despooling Data");
433       break;
434     case JS_AttrDespooling:
435       jobstat = _("SD despooling Attributes");
436       break;
437     case JS_AttrInserting:
438       jobstat = _("Dir inserting Attributes");
439       break;
440     default:
441       if (JobStatus == 0) {
442         buf[0] = 0;
443       } else {
444         Bsnprintf(buf, sizeof(buf), _("Unknown Job termination status=%d"),
445                   JobStatus);
446       }
447       jobstat = buf;
448       break;
449   }
450   bstrncpy(msg, jobstat, maxlen);
451 }
452 
453 /*
454  * Convert a JobStatus code into a human readable form - gui version
455  */
JobstatusToAsciiGui(int JobStatus,char * msg,int maxlen)456 void JobstatusToAsciiGui(int JobStatus, char* msg, int maxlen)
457 {
458   const char* cnv = NULL;
459   switch (JobStatus) {
460     case JS_Terminated:
461       cnv = _("Completed successfully");
462       break;
463     case JS_Warnings:
464       cnv = _("Completed with warnings");
465       break;
466     case JS_ErrorTerminated:
467       cnv = _("Terminated with errors");
468       break;
469     case JS_FatalError:
470       cnv = _("Fatal error");
471       break;
472     case JS_Created:
473       cnv = _("Created, not yet running");
474       break;
475     case JS_Canceled:
476       cnv = _("Canceled by user");
477       break;
478     case JS_Differences:
479       cnv = _("Verify found differences");
480       break;
481     case JS_WaitFD:
482       cnv = _("Waiting for File daemon");
483       break;
484     case JS_WaitSD:
485       cnv = _("Waiting for Storage daemon");
486       break;
487     case JS_WaitPriority:
488       cnv = _("Waiting for higher priority jobs");
489       break;
490     case JS_AttrInserting:
491       cnv = _("Batch inserting file records");
492       break;
493   }
494 
495   if (cnv) {
496     bstrncpy(msg, cnv, maxlen);
497   } else {
498     JobstatusToAscii(JobStatus, msg, maxlen);
499   }
500 }
501 
502 /*
503  * Convert Job Termination Status into a string
504  */
job_status_to_str(int stat)505 const char* job_status_to_str(int stat)
506 {
507   const char* str;
508 
509   switch (stat) {
510     case JS_Terminated:
511       str = _("OK");
512       break;
513     case JS_Warnings:
514       str = _("OK -- with warnings");
515       break;
516     case JS_ErrorTerminated:
517     case JS_Error:
518       str = _("Error");
519       break;
520     case JS_FatalError:
521       str = _("Fatal Error");
522       break;
523     case JS_Canceled:
524       str = _("Canceled");
525       break;
526     case JS_Differences:
527       str = _("Differences");
528       break;
529     default:
530       str = _("Unknown term code");
531       break;
532   }
533   return str;
534 }
535 
536 /*
537  * Convert Job Type into a string
538  */
job_type_to_str(int type)539 const char* job_type_to_str(int type)
540 {
541   const char* str = NULL;
542 
543   switch (type) {
544     case JT_BACKUP:
545       str = _("Backup");
546       break;
547     case JT_MIGRATED_JOB:
548       str = _("Migrated Job");
549       break;
550     case JT_VERIFY:
551       str = _("Verify");
552       break;
553     case JT_RESTORE:
554       str = _("Restore");
555       break;
556     case JT_CONSOLE:
557       str = _("Console");
558       break;
559     case JT_SYSTEM:
560       str = _("System or Console");
561       break;
562     case JT_ADMIN:
563       str = _("Admin");
564       break;
565     case JT_ARCHIVE:
566       str = _("Archive");
567       break;
568     case JT_JOB_COPY:
569       str = _("Job Copy");
570       break;
571     case JT_COPY:
572       str = _("Copy");
573       break;
574     case JT_MIGRATE:
575       str = _("Migrate");
576       break;
577     case JT_SCAN:
578       str = _("Scan");
579       break;
580     case JT_CONSOLIDATE:
581       str = _("Consolidate");
582       break;
583   }
584   if (!str) { str = _("Unknown Type"); }
585   return str;
586 }
587 
588 /*
589  * Convert ActionOnPurge to string (Truncate, Erase, Destroy)
590  */
action_on_purge_to_string(int aop,PoolMem & ret)591 char* action_on_purge_to_string(int aop, PoolMem& ret)
592 {
593   if (aop & ON_PURGE_TRUNCATE) { PmStrcpy(ret, _("Truncate")); }
594   if (!aop) { PmStrcpy(ret, _("None")); }
595   return ret.c_str();
596 }
597 
598 /*
599  * Convert Job Level into a string
600  */
job_level_to_str(int level)601 const char* job_level_to_str(int level)
602 {
603   const char* str;
604 
605   switch (level) {
606     case L_BASE:
607       str = _("Base");
608       break;
609     case L_FULL:
610       str = _("Full");
611       break;
612     case L_INCREMENTAL:
613       str = _("Incremental");
614       break;
615     case L_DIFFERENTIAL:
616       str = _("Differential");
617       break;
618     case L_SINCE:
619       str = _("Since");
620       break;
621     case L_VERIFY_CATALOG:
622       str = _("Verify Catalog");
623       break;
624     case L_VERIFY_INIT:
625       str = _("Verify Init Catalog");
626       break;
627     case L_VERIFY_VOLUME_TO_CATALOG:
628       str = _("Verify Volume to Catalog");
629       break;
630     case L_VERIFY_DISK_TO_CATALOG:
631       str = _("Verify Disk to Catalog");
632       break;
633     case L_VERIFY_DATA:
634       str = _("Verify Data");
635       break;
636     case L_VIRTUAL_FULL:
637       str = _("Virtual Full");
638       break;
639     case L_NONE:
640       str = " ";
641       break;
642     default:
643       str = _("Unknown Job Level");
644       break;
645   }
646   return str;
647 }
648 
volume_status_to_str(const char * status)649 const char* volume_status_to_str(const char* status)
650 {
651   int pos;
652   const char* vs[] = {NT_("Append"), _("Append"),      NT_("Archive"),
653                       _("Archive"),  NT_("Disabled"),  _("Disabled"),
654                       NT_("Full"),   _("Full"),        NT_("Used"),
655                       _("Used"),     NT_("Cleaning"),  _("Cleaning"),
656                       NT_("Purged"), _("Purged"),      NT_("Recycle"),
657                       _("Recycle"),  NT_("Read-Only"), _("Read-Only"),
658                       NT_("Error"),  _("Error"),       NULL,
659                       NULL};
660 
661   if (status) {
662     for (pos = 0; vs[pos]; pos += 2) {
663       if (bstrcmp(vs[pos], status)) { return vs[pos + 1]; }
664     }
665   }
666 
667   return _("Invalid volume status");
668 }
669 
670 
671 /*
672  * Encode the mode bits into a 10 character string like LS does
673  */
encode_mode(mode_t mode,char * buf)674 char* encode_mode(mode_t mode, char* buf)
675 {
676   char* cp = buf;
677 
678   *cp++ =
679       S_ISDIR(mode)
680           ? 'd'
681           : S_ISBLK(mode)
682                 ? 'b'
683                 : S_ISCHR(mode)
684                       ? 'c'
685                       : S_ISLNK(mode)
686                             ? 'l'
687                             : S_ISFIFO(mode) ? 'f' : S_ISSOCK(mode) ? 's' : '-';
688   *cp++ = mode & S_IRUSR ? 'r' : '-';
689   *cp++ = mode & S_IWUSR ? 'w' : '-';
690   *cp++ = (mode & S_ISUID ? (mode & S_IXUSR ? 's' : 'S')
691                           : (mode & S_IXUSR ? 'x' : '-'));
692   *cp++ = mode & S_IRGRP ? 'r' : '-';
693   *cp++ = mode & S_IWGRP ? 'w' : '-';
694   *cp++ = (mode & S_ISGID ? (mode & S_IXGRP ? 's' : 'S')
695                           : (mode & S_IXGRP ? 'x' : '-'));
696   *cp++ = mode & S_IROTH ? 'r' : '-';
697   *cp++ = mode & S_IWOTH ? 'w' : '-';
698   *cp++ = (mode & S_ISVTX ? (mode & S_IXOTH ? 't' : 'T')
699                           : (mode & S_IXOTH ? 'x' : '-'));
700   *cp = '\0';
701   return cp;
702 }
703 
704 #if defined(HAVE_WIN32)
DoShellExpansion(char * name,int name_len)705 int DoShellExpansion(char* name, int name_len)
706 {
707   char* src = strdup(name);
708 
709   ExpandEnvironmentStrings(src, name, name_len);
710 
711   free(src);
712 
713   return 1;
714 }
715 #else
DoShellExpansion(char * name,int name_len)716 int DoShellExpansion(char* name, int name_len)
717 {
718   static char meta[] = "~\\$[]*?`'<>\"";
719   bool found = false;
720   int len, i, status;
721   POOLMEM *cmd, *line;
722   Bpipe* bpipe;
723   const char* shellcmd;
724 
725   /*
726    * Check if any meta characters are present
727    */
728   len = strlen(meta);
729   for (i = 0; i < len; i++) {
730     if (strchr(name, meta[i])) {
731       found = true;
732       break;
733     }
734   }
735   if (found) {
736     cmd = GetPoolMemory(PM_FNAME);
737     line = GetPoolMemory(PM_FNAME);
738     /*
739      * Look for shell
740      */
741     if ((shellcmd = getenv("SHELL")) == NULL) { shellcmd = "/bin/sh"; }
742     PmStrcpy(cmd, shellcmd);
743     PmStrcat(cmd, " -c \"echo ");
744     PmStrcat(cmd, name);
745     PmStrcat(cmd, "\"");
746     Dmsg1(400, "Send: %s\n", cmd);
747     if ((bpipe = OpenBpipe(cmd, 0, "r"))) {
748       bfgets(line, bpipe->rfd);
749       StripTrailingJunk(line);
750       status = CloseBpipe(bpipe);
751       Dmsg2(400, "status=%d got: %s\n", status, line);
752     } else {
753       status = 1; /* error */
754     }
755     FreePoolMemory(cmd);
756     FreePoolMemory(line);
757     if (status == 0) { bstrncpy(name, line, name_len); }
758   }
759   return 1;
760 }
761 #endif
762 
763 /*
764  * MAKESESSIONKEY  --  Generate session key with optional start
765  *                     key.  If mode is TRUE, the key will be
766  *                     translated to a string, otherwise it is
767  *                     returned as 16 binary bytes.
768  *
769  *  from SpeakFreely by John Walker
770  */
MakeSessionKey(char * key,char * seed,int mode)771 void MakeSessionKey(char* key, char* seed, int mode)
772 {
773   int j, k;
774   MD5_CTX md5c;
775   unsigned char md5key[16], md5key1[16];
776   char s[1024];
777 
778 #define ss sizeof(s)
779 
780   s[0] = 0;
781   if (seed != NULL) { bstrncat(s, seed, sizeof(s)); }
782 
783   /*
784    * The following creates a seed for the session key generator
785    * based on a collection of volatile and environment-specific
786    * information unlikely to be vulnerable (as a whole) to an
787    * exhaustive search attack.  If one of these items isn't
788    * available on your machine, replace it with something
789    * equivalent or, if you like, just delete it.
790    */
791 #if defined(HAVE_WIN32)
792   {
793     LARGE_INTEGER li;
794     DWORD length;
795     FILETIME ft;
796 
797     Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetCurrentProcessId());
798     (void)getcwd(s + strlen(s), 256);
799     Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)GetTickCount());
800     QueryPerformanceCounter(&li);
801     Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)li.LowPart);
802     GetSystemTimeAsFileTime(&ft);
803     Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwLowDateTime);
804     Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)ft.dwHighDateTime);
805     length = 256;
806     GetComputerName(s + strlen(s), &length);
807     length = 256;
808     GetUserName(s + strlen(s), &length);
809   }
810 #else
811   Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getpid());
812   Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getppid());
813   (void)getcwd(s + strlen(s), 256);
814   Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)clock());
815   Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)time(NULL));
816 #if defined(Solaris)
817   sysinfo(SI_HW_SERIAL, s + strlen(s), 12);
818 #endif
819 #if defined(HAVE_GETHOSTID)
820   Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)gethostid());
821 #endif
822   gethostname(s + strlen(s), 256);
823   Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getuid());
824   Bsnprintf(s + strlen(s), ss, "%lu", (uint32_t)getgid());
825 #endif
826   MD5_Init(&md5c);
827   MD5_Update(&md5c, (uint8_t*)s, strlen(s));
828   MD5_Final(md5key, &md5c);
829   Bsnprintf(s + strlen(s), ss, "%lu",
830             (uint32_t)((time(NULL) + 65121) ^ 0x375F));
831   MD5_Init(&md5c);
832   MD5_Update(&md5c, (uint8_t*)s, strlen(s));
833   MD5_Final(md5key1, &md5c);
834 #define nextrand (md5key[j] ^ md5key1[j])
835   if (mode) {
836     for (j = k = 0; j < 16; j++) {
837       unsigned char rb = nextrand;
838 
839 #define Rad16(x) ((x) + 'A')
840       key[k++] = Rad16((rb >> 4) & 0xF);
841       key[k++] = Rad16(rb & 0xF);
842 #undef Rad16
843       if (j & 1) { key[k++] = '-'; }
844     }
845     key[--k] = 0;
846   } else {
847     for (j = 0; j < 16; j++) { key[j] = nextrand; }
848   }
849 }
850 #undef nextrand
851 
EncodeSessionKey(char * encode,char * session,char * key,int maxlen)852 void EncodeSessionKey(char* encode, char* session, char* key, int maxlen)
853 {
854   int i;
855 
856   for (i = 0; (i < maxlen - 1) && session[i]; i++) {
857     if (session[i] == '-') {
858       encode[i] = '-';
859     } else {
860       encode[i] = ((session[i] - 'A' + key[i]) & 0xF) + 'A';
861     }
862   }
863   encode[i] = 0;
864   Dmsg3(000, "Session=%s key=%s encode=%s\n", session, key, encode);
865 }
866 
DecodeSessionKey(char * decode,char * session,char * key,int maxlen)867 void DecodeSessionKey(char* decode, char* session, char* key, int maxlen)
868 {
869   int i, x;
870 
871   for (i = 0; (i < maxlen - 1) && session[i]; i++) {
872     if (session[i] == '-') {
873       decode[i] = '-';
874     } else {
875       x = (session[i] - 'A' - key[i]) & 0xF;
876       if (x < 0) { x += 16; }
877       decode[i] = x + 'A';
878     }
879   }
880   decode[i] = 0;
881   Dmsg3(000, "Session=%s key=%s decode=%s\n", session, key, decode);
882 }
883 
884 /*
885  * Edit job codes into main command line
886  *  %% = %
887  *  %B = Job Bytes in human readable format
888  *  %F = Job Files
889  *  %P = Pid of daemon
890  *  %b = Job Bytes
891  *  %c = Client's name
892  *  %d = Director's name
893  *  %e = Job Exit code
894  *  %i = JobId
895  *  %j = Unique Job id
896  *  %l = job level
897  *  %n = Unadorned Job name
898  *  %r = Recipients
899  *  %s = Since time
900  *  %t = Job type (Backup, ...)
901  *  %v = Volume name(s)
902  *
903  *  omsg = edited output message
904  *  imsg = input string containing edit codes (%x)
905  *  to = recipients list
906  */
edit_job_codes(JobControlRecord * jcr,char * omsg,const char * imsg,const char * to,job_code_callback_t callback)907 POOLMEM* edit_job_codes(JobControlRecord* jcr,
908                         char* omsg,
909                         const char* imsg,
910                         const char* to,
911                         job_code_callback_t callback)
912 {
913   const char* p;
914   char* q;
915   const char* str;
916   char ed1[50];
917   char add[50];
918   char name[MAX_NAME_LENGTH];
919   int i;
920 
921   *omsg = 0;
922   Dmsg1(200, "edit_job_codes: %s\n", imsg);
923   for (p = imsg; *p; p++) {
924     if (*p == '%') {
925       switch (*++p) {
926         case '%':
927           str = "%";
928           break;
929         case 'B': /* Job Bytes in human readable format */
930           if (jcr) {
931             Bsnprintf(add, sizeof(add), "%sB",
932                       edit_uint64_with_suffix(jcr->JobBytes, ed1));
933             str = add;
934           } else {
935             str = _("*None*");
936           }
937           break;
938         case 'F': /* Job Files */
939           if (jcr) {
940             str = edit_uint64(jcr->JobFiles, add);
941           } else {
942             str = _("*None*");
943           }
944           break;
945         case 'P': /* Process Id */
946           Bsnprintf(add, sizeof(add), "%lu", (uint32_t)getpid());
947           str = add;
948           break;
949         case 'b': /* Job Bytes */
950           if (jcr) {
951             str = edit_uint64(jcr->JobBytes, add);
952           } else {
953             str = _("*None*");
954           }
955           break;
956         case 'c': /* Client's name */
957           if (jcr && jcr->client_name) {
958             str = jcr->client_name;
959           } else {
960             str = _("*None*");
961           }
962           break;
963         case 'd': /* Director's name */
964           str = my_name;
965           break;
966         case 'e': /* Job Exit code */
967           if (jcr) {
968             str = job_status_to_str(jcr->JobStatus);
969           } else {
970             str = _("*None*");
971           }
972           break;
973         case 'i': /* JobId */
974           if (jcr) {
975             Bsnprintf(add, sizeof(add), "%d", jcr->JobId);
976             str = add;
977           } else {
978             str = _("*None*");
979           }
980           break;
981         case 'j': /* Job name */
982           if (jcr) {
983             str = jcr->Job;
984           } else {
985             str = _("*None*");
986           }
987           break;
988         case 'l': /* Job level */
989           if (jcr) {
990             str = job_level_to_str(jcr->getJobLevel());
991           } else {
992             str = _("*None*");
993           }
994           break;
995         case 'n': /* Unadorned Job name */
996           if (jcr) {
997             bstrncpy(name, jcr->Job, sizeof(name));
998             /*
999              * There are three periods after the Job name
1000              */
1001             for (i = 0; i < 3; i++) {
1002               if ((q = strrchr(name, '.')) != NULL) { *q = 0; }
1003             }
1004             str = name;
1005           } else {
1006             str = _("*None*");
1007           }
1008           break;
1009         case 'r': /* Recipients */
1010           str = to;
1011           break;
1012         case 's': /* Since time */
1013           if (jcr && jcr->stime) {
1014             str = jcr->stime;
1015           } else {
1016             str = _("*None*");
1017           }
1018           break;
1019         case 't': /* Job type */
1020           if (jcr) {
1021             str = job_type_to_str(jcr->getJobType());
1022           } else {
1023             str = _("*None*");
1024           }
1025           break;
1026         case 'v': /* Volume name(s) */
1027           if (jcr) {
1028             if (jcr->VolumeName) {
1029               str = jcr->VolumeName;
1030             } else {
1031               str = _("*None*");
1032             }
1033           } else {
1034             str = _("*None*");
1035           }
1036           break;
1037         default:
1038           str = NULL;
1039           if (callback != NULL) { str = callback(jcr, p); }
1040 
1041           if (!str) {
1042             add[0] = '%';
1043             add[1] = *p;
1044             add[2] = 0;
1045             str = add;
1046           }
1047           break;
1048       }
1049     } else {
1050       add[0] = *p;
1051       add[1] = 0;
1052       str = add;
1053     }
1054     Dmsg1(1200, "add_str %s\n", str);
1055     PmStrcat(omsg, str);
1056     Dmsg1(1200, "omsg=%s\n", omsg);
1057   }
1058 
1059   return omsg;
1060 }
1061 
SetWorkingDirectory(const char * wd)1062 void SetWorkingDirectory(const char* wd)
1063 {
1064   struct stat stat_buf;
1065 
1066   if (wd == NULL) {
1067     Emsg0(M_ERROR_TERM, 0,
1068           _("Working directory not defined. Cannot continue.\n"));
1069   }
1070   if (stat(wd, &stat_buf) != 0) {
1071     Emsg1(M_ERROR_TERM, 0,
1072           _("Working Directory: \"%s\" not found. Cannot continue.\n"), wd);
1073   }
1074   if (!S_ISDIR(stat_buf.st_mode)) {
1075     Emsg1(M_ERROR_TERM, 0,
1076           _("Working Directory: \"%s\" is not a directory. Cannot continue.\n"),
1077           wd);
1078   }
1079   working_directory = wd; /* set global */
1080 }
1081 
last_path_separator(const char * str)1082 const char* last_path_separator(const char* str)
1083 {
1084   if (*str != '\0') {
1085     for (const char* p = &str[strlen(str) - 1]; p >= str; p--) {
1086       if (IsPathSeparator(*p)) { return p; }
1087     }
1088   }
1089   return NULL;
1090 }
1091 
StringToLowerCase(std::string & s)1092 void StringToLowerCase(std::string& s)
1093 {
1094   for (auto& c : s) { c = std::tolower(c); }
1095 }
1096 
StringToLowerCase(std::string & out,const std::string & in)1097 void StringToLowerCase(std::string& out, const std::string& in)
1098 {
1099   out.clear();
1100   for (const auto& c : in) { out += std::tolower(c); }
1101 }
1102 
SortCaseInsensitive(std::vector<std::string> & v)1103 void SortCaseInsensitive(std::vector<std::string>& v)
1104 {
1105   if (v.empty()) { return; }
1106 
1107   std::sort(v.begin(), v.end(), [](const std::string& a, const std::string& b) {
1108     std::string x{a}, y{b};
1109     StringToLowerCase(x);
1110     StringToLowerCase(y);
1111     return x < y;
1112   });
1113 }
1114 
getenv_std_string(std::string env_var)1115 std::string getenv_std_string(std::string env_var)
1116 {
1117   const char* v = (std::getenv(env_var.c_str()));
1118   return v ? std::string(v) : std::string();
1119 }
1120