1 /**
2  * settings.c -- goaccess configuration
3  *    ______      ___
4  *   / ____/___  /   | _____________  __________
5  *  / / __/ __ \/ /| |/ ___/ ___/ _ \/ ___/ ___/
6  * / /_/ / /_/ / ___ / /__/ /__/  __(__  |__  )
7  * \____/\____/_/  |_\___/\___/\___/____/____/
8  *
9  * The MIT License (MIT)
10  * Copyright (c) 2009-2020 Gerardo Orellana <hello @ goaccess.io>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy
13  * of this software and associated documentation files (the "Software"), to deal
14  * in the Software without restriction, including without limitation the rights
15  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16  * copies of the Software, and to permit persons to whom the Software is
17  * furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in all
20  * copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28  * SOFTWARE.
29  */
30 
31 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
32 
33 #include <ctype.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <errno.h>
39 
40 #include "settings.h"
41 
42 #include "error.h"
43 #include "gkhash.h"
44 #include "labels.h"
45 #include "pdjson.h"
46 #include "util.h"
47 #include "xmalloc.h"
48 
49 static char **nargv;
50 static int nargc = 0;
51 
52 /* *INDENT-OFF* */
53 static GEnum LOGTYPE[] = {
54   {"COMBINED"     , COMBINED}     ,
55   {"VCOMBINED"    , VCOMBINED}    ,
56   {"COMMON"       , COMMON}       ,
57   {"VCOMMON"      , VCOMMON}      ,
58   {"W3C"          , W3C}          ,
59   {"SQUID"        , SQUID}        ,
60   {"CLOUDFRONT"   , CLOUDFRONT}   ,
61   {"CLOUDSTORAGE" , CLOUDSTORAGE} ,
62   {"AWSELB"       , AWSELB}       ,
63   {"AWSS3"        , AWSS3}        ,
64   {"CADDY"        , CADDY}        ,
65 };
66 
67 static const GPreConfLog logs = {
68   "%h %^[%d:%t %^] \"%r\" %s %b \"%R\" \"%u\"",                 /* NCSA */
69   "%v:%^ %h %^[%d:%t %^] \"%r\" %s %b \"%R\" \"%u\"",           /* NCSA + VHost  */
70   "%h %^[%d:%t %^] \"%r\" %s %b",                               /* CLF */
71   "%v:%^ %h %^[%d:%t %^] \"%r\" %s %b",                         /* CLF+VHost */
72   "%d %t %^ %m %U %q %^ %^ %h %u %R %s %^ %^ %L",               /* W3C */
73   "%d\\t%t\\t%^\\t%b\\t%h\\t%m\\t%v\\t%U\\t%s\\t%R\\t%u\\t%q\\t%^\\t%C\\t%^\\t%^\\t%^\\t%^\\t%T\\t%^\\t%K\\t%k\\t%^\\t%H\\t%^",  /* CloudFront */
74   "\"%x\",\"%h\",%^,%^,\"%m\",\"%U\",\"%s\",%^,\"%b\",\"%D\",%^,\"%R\",\"%u\"", /* Cloud Storage */
75   "%^ %dT%t.%^ %v %h:%^ %^ %T %^ %^ %s %^ %b %^ \"%r\" \"%u\" %^",    /* AWS Elastic Load Balancing */
76   "%^ %^ %^ %v %^: %x.%^ %~%L %h %^/%s %b %m %U",               /* Squid Native */
77   "%^ %v [%d:%t %^] %h %^\"%r\" %s %^ %b %^ %L %^ \"%R\" \"%u\"", /* Amazon S3 */
78 
79   /* Caddy JSON */
80   "{ \"ts\": \"%x.%^\", \"request\": { \"remote_addr\": \"%h:%^\", \"proto\":"
81   "\"%H\", \"method\": \"%m\", \"host\": \"%v\", \"uri\": \"%U\", \"headers\": {"
82   "\"User-Agent\": [\"%u\"], \"Referer\": [\"%R\"] }, \"tls\": { \"cipher_suite\":"
83   "\"%k\", \"proto\": \"%K\" } }, \"duration\": \"%T\", \"size\": \"%b\","
84   "\"status\": \"%s\", \"resp_headers\": { \"Content-Type\": [\"%M\"] } }"
85 };
86 
87 static const GPreConfTime times = {
88   "%H:%M:%S",
89   "%f",       /* Cloud Storage (usec) */
90   "%s",       /* Squid (sec) */
91 };
92 
93 static const GPreConfDate dates = {
94   "%d/%b/%Y", /* Apache */
95   "%Y-%m-%d", /* W3C */
96   "%f",       /* Cloud Storage (usec) */
97   "%s",       /* Squid (sec) */
98 };
99 /* *INDENT-ON* */
100 
101 /* Ignore the following options */
102 static const char *ignore_cmd_opts[] = {
103   "help",
104   "storage",
105   "version",
106 };
107 
108 /* Determine if the given command line option needs to be ignored.
109  *
110  * If needs to be ignored, 1 is returned.
111  * If not within the list of ignored command line options, 0 is returned. */
112 static int
in_ignore_cmd_opts(const char * val)113 in_ignore_cmd_opts (const char *val) {
114   size_t i;
115   for (i = 0; i < ARRAY_SIZE (ignore_cmd_opts); i++) {
116     if (strstr (val, ignore_cmd_opts[i]) != NULL)
117       return 1;
118   }
119   return 0;
120 }
121 
122 /* Get the location of the configuration file.
123  *
124  * By default, it attempts to read it from the user supplied path, else it will
125  * try to open the global config file path (sysconfdir) or from the HOME
126  * environment variable (~/.goaccessrc).
127  *
128  * On success, the path to the configuration file is returned. */
129 char *
get_config_file_path(void)130 get_config_file_path (void) {
131   char *upath = NULL, *gpath = NULL, *rpath = NULL;
132 
133   /* determine which config file to open, default or custom */
134   if (conf.iconfigfile != NULL) {
135     rpath = realpath (conf.iconfigfile, NULL);
136     if (rpath == NULL)
137       FATAL ("Unable to open the specified config file. %s", strerror (errno));
138     return rpath;
139   }
140 
141   /* first attempt to use the user's config file, e.g., ~/.goaccessrc */
142   upath = get_user_config ();
143   /* failure, e.g. if the file does not exist */
144   if ((rpath = realpath (upath, NULL)) != NULL) {
145     free (upath);
146     return rpath;
147   }
148   LOG_DEBUG (("Unable to find user's config file %s %s", upath, strerror (errno)));
149   free (upath);
150 
151   /* otherwise, fallback to global config file, e.g.,%sysconfdir%/goaccess.conf */
152   gpath = get_global_config ();
153   if ((rpath = realpath (gpath, NULL)) != NULL && conf.load_global_config) {
154     free (gpath);
155     return rpath;
156   }
157   LOG_DEBUG (("Unable to find global config file %s %s", gpath, strerror (errno)));
158   free (gpath);
159 
160   return NULL;
161 }
162 
163 /* Use predefined static files when no config file is used. Note that
164  * the order in which are listed is from the most to the least common
165  * (most cases). */
166 void
set_default_static_files(void)167 set_default_static_files (void) {
168   size_t i;
169   const char *exts[] = {
170     ".css",
171     ".js ",
172     ".jpg",
173     ".png",
174     ".gif",
175     ".ico",
176     ".jpeg",
177     ".pdf",
178     ".txt",
179     ".csv",
180     ".mpeg",
181     ".mpg",
182     ".swf",
183     ".woff",
184     ".woff2",
185     ".xls",
186     ".xlsx",
187     ".doc ",
188     ".docx",
189     ".ppt ",
190     ".pptx",
191     ".zip",
192     ".mp3",
193     ".mp4",
194     ".exe",
195     ".iso ",
196     ".gz  ",
197     ".rar ",
198     ".svg ",
199     ".bmp ",
200     ".tar ",
201     ".tgz ",
202     ".tiff",
203     ".tif ",
204     ".ttf ",
205     ".flv ",
206   };
207 
208   if (conf.static_file_idx > 0)
209     return;
210 
211   for (i = 0; i < ARRAY_SIZE (exts); i++) {
212     if (conf.static_file_max_len < strlen (exts[i]))
213       conf.static_file_max_len = strlen (exts[i]);
214     conf.static_files[conf.static_file_idx++] = exts[i];
215   }
216 }
217 
218 /* Clean malloc'd log/date/time escaped formats. */
219 void
free_formats(void)220 free_formats (void) {
221   free (conf.log_format);
222   free (conf.date_format);
223   free (conf.date_num_format);
224   free (conf.spec_date_time_format);
225   free (conf.spec_date_time_num_format);
226   free (conf.time_format);
227 }
228 
229 /* Clean malloc'd command line arguments. */
230 void
free_cmd_args(void)231 free_cmd_args (void) {
232   int i;
233   if (nargc == 0)
234     return;
235   for (i = 0; i < nargc; i++)
236     free (nargv[i]);
237   free (nargv);
238   free (conf.iconfigfile);
239 }
240 
241 /* Append extra value to argv */
242 static void
append_to_argv(int * argc,char *** argv,char * val)243 append_to_argv (int *argc, char ***argv, char *val) {
244   char **_argv = xrealloc (*argv, (*argc + 2) * sizeof (*_argv));
245   _argv[*argc] = val;
246   _argv[*argc + 1] = (char *) '\0';
247   (*argc)++;
248   *argv = _argv;
249 }
250 
251 /* Parses the configuration file to feed getopt_long.
252  *
253  * On error, ENOENT error code is returned.
254  * On success, 0 is returned and config file enabled options are appended to
255  * argv. */
256 int
parse_conf_file(int * argc,char *** argv)257 parse_conf_file (int *argc, char ***argv) {
258   char line[MAX_LINE_CONF + 1];
259   char *path = NULL, *val, *opt, *p;
260   FILE *file;
261   int i, n = 0;
262   size_t idx;
263 
264   /* assumes program name is on argv[0], though, it is not guaranteed */
265   append_to_argv (&nargc, &nargv, xstrdup ((char *) *argv[0]));
266 
267   /* determine which config file to open, default or custom */
268   path = get_config_file_path ();
269   if (path == NULL)
270     return ENOENT;
271 
272   /* could not open conf file, if so prompt conf dialog */
273   if ((file = fopen (path, "r")) == NULL) {
274     free (path);
275     return ENOENT;
276   }
277 
278   while (fgets (line, sizeof line, file) != NULL) {
279     while (line[0] == ' ' || line[0] == '\t')
280       memmove (line, line + 1, strlen (line));
281     n++;
282     if (line[0] == '\n' || line[0] == '\r' || line[0] == '#')
283       continue;
284 
285     /* key */
286     idx = strcspn (line, " \t");
287     if (strlen (line) == idx)
288       FATAL ("Malformed config key at line: %d", n);
289 
290     line[idx] = '\0';
291 
292     /* make old config options backwards compatible by
293      * substituting underscores with dashes */
294     while ((p = strpbrk (line, "_")) != NULL)
295       *p = '-';
296 
297     /* Ignore the following options when reading the config file */
298     if (in_ignore_cmd_opts (line))
299       continue;
300 
301     /* value */
302     val = line + (idx + 1);
303     idx = strspn (val, " \t");
304     if (strlen (line) == idx)
305       FATAL ("Malformed config value at line: %d", n);
306     val = val + idx;
307     val = trim_str (val);
308 
309     if (strcmp ("false", val) == 0)
310       continue;
311 
312     /* set it as command line options */
313     opt = xmalloc (snprintf (NULL, 0, "--%s", line) + 1);
314     sprintf (opt, "--%s", line);
315 
316     append_to_argv (&nargc, &nargv, opt);
317     if (strcmp ("true", val) != 0)
318       append_to_argv (&nargc, &nargv, xstrdup (val));
319   }
320 
321   /* give priority to command line arguments */
322   for (i = 1; i < *argc; i++)
323     append_to_argv (&nargc, &nargv, xstrdup ((char *) (*argv)[i]));
324 
325   *argc = nargc;
326   *argv = (char **) nargv;
327 
328   fclose (file);
329 
330   if (conf.iconfigfile == NULL)
331     conf.iconfigfile = xstrdup (path);
332 
333   free (path);
334   return 0;
335 }
336 
337 /* Get the enumerated log format given its equivalent format string.
338  * The case in the format string does not matter.
339  *
340  * On error, -1 is returned.
341  * On success, the enumerated format is returned. */
342 static int
get_log_format_item_enum(const char * str)343 get_log_format_item_enum (const char *str) {
344   int ret;
345   char *upstr;
346 
347   ret = str2enum (LOGTYPE, ARRAY_SIZE (LOGTYPE), str);
348   if (ret >= 0)
349     return ret;
350 
351   /* uppercase the input string and try again */
352   upstr = strtoupper (xstrdup (str));
353   ret = str2enum (LOGTYPE, ARRAY_SIZE (LOGTYPE), upstr);
354   free (upstr);
355 
356   return ret;
357 }
358 
359 /* Determine the selected log format from the config file or command line
360  * option.
361  *
362  * On error, -1 is returned.
363  * On success, the index of the matched item is returned. */
364 size_t
get_selected_format_idx(void)365 get_selected_format_idx (void) {
366   if (conf.log_format == NULL)
367     return (size_t) -1;
368   if (strcmp (conf.log_format, logs.common) == 0)
369     return COMMON;
370   else if (strcmp (conf.log_format, logs.vcommon) == 0)
371     return VCOMMON;
372   else if (strcmp (conf.log_format, logs.combined) == 0)
373     return COMBINED;
374   else if (strcmp (conf.log_format, logs.vcombined) == 0)
375     return VCOMBINED;
376   else if (strcmp (conf.log_format, logs.w3c) == 0)
377     return W3C;
378   else if (strcmp (conf.log_format, logs.cloudfront) == 0)
379     return CLOUDFRONT;
380   else if (strcmp (conf.log_format, logs.cloudstorage) == 0)
381     return CLOUDSTORAGE;
382   else if (strcmp (conf.log_format, logs.awselb) == 0)
383     return AWSELB;
384   else if (strcmp (conf.log_format, logs.squid) == 0)
385     return SQUID;
386   else if (strcmp (conf.log_format, logs.awss3) == 0)
387     return AWSS3;
388   else if (strcmp (conf.log_format, logs.caddy) == 0)
389     return CADDY;
390   else
391     return (size_t) -1;
392 }
393 
394 /* Determine the selected log format from the config file or command line
395  * option.
396  *
397  * On error, NULL is returned.
398  * On success, an allocated string containing the log format is returned. */
399 char *
get_selected_format_str(size_t idx)400 get_selected_format_str (size_t idx) {
401   char *fmt = NULL;
402   switch (idx) {
403   case COMMON:
404     fmt = alloc_string (logs.common);
405     break;
406   case VCOMMON:
407     fmt = alloc_string (logs.vcommon);
408     break;
409   case COMBINED:
410     fmt = alloc_string (logs.combined);
411     break;
412   case VCOMBINED:
413     fmt = alloc_string (logs.vcombined);
414     break;
415   case W3C:
416     fmt = alloc_string (logs.w3c);
417     break;
418   case CLOUDFRONT:
419     fmt = alloc_string (logs.cloudfront);
420     break;
421   case CLOUDSTORAGE:
422     fmt = alloc_string (logs.cloudstorage);
423     break;
424   case AWSELB:
425     fmt = alloc_string (logs.awselb);
426     break;
427   case SQUID:
428     fmt = alloc_string (logs.squid);
429     break;
430   case AWSS3:
431     fmt = alloc_string (logs.awss3);
432     break;
433   case CADDY:
434     fmt = alloc_string (logs.caddy);
435     break;
436   }
437 
438   return fmt;
439 }
440 
441 /* Determine the selected date format from the config file or command line
442  * option.
443  *
444  * On error, NULL is returned.
445  * On success, an allocated string containing the date format is returned. */
446 char *
get_selected_date_str(size_t idx)447 get_selected_date_str (size_t idx) {
448   char *fmt = NULL;
449   switch (idx) {
450   case COMMON:
451   case VCOMMON:
452   case COMBINED:
453   case VCOMBINED:
454   case AWSS3:
455     fmt = alloc_string (dates.apache);
456     break;
457   case AWSELB:
458   case CLOUDFRONT:
459   case W3C:
460     fmt = alloc_string (dates.w3c);
461     break;
462   case CLOUDSTORAGE:
463     fmt = alloc_string (dates.usec);
464     break;
465   case SQUID:
466   case CADDY:
467     fmt = alloc_string (dates.sec);
468     break;
469   }
470 
471   return fmt;
472 }
473 
474 /* Determine the selected time format from the config file or command line
475  * option.
476  *
477  * On error, NULL is returned.
478  * On success, an allocated string containing the time format is returned. */
479 char *
get_selected_time_str(size_t idx)480 get_selected_time_str (size_t idx) {
481   char *fmt = NULL;
482   switch (idx) {
483   case AWSELB:
484   case CLOUDFRONT:
485   case COMBINED:
486   case COMMON:
487   case VCOMBINED:
488   case VCOMMON:
489   case W3C:
490   case AWSS3:
491     fmt = alloc_string (times.fmt24);
492     break;
493   case CLOUDSTORAGE:
494     fmt = alloc_string (times.usec);
495     break;
496   case SQUID:
497   case CADDY:
498     fmt = alloc_string (times.sec);
499     break;
500   }
501 
502   return fmt;
503 }
504 
505 /* Determine if the log/date/time were set, otherwise exit the program
506  * execution. */
507 const char *
verify_formats(void)508 verify_formats (void) {
509   if (conf.time_format == NULL || *conf.time_format == '\0')
510     return ERR_FORMAT_NO_TIME_FMT;
511 
512   if (conf.date_format == NULL || *conf.date_format == '\0')
513     return ERR_FORMAT_NO_DATE_FMT;
514 
515   if (conf.log_format == NULL || *conf.log_format == '\0')
516     return ERR_FORMAT_NO_LOG_FMT;
517 
518   return NULL;
519 }
520 
521 /* A wrapper function to concat the given specificity to the date
522  * format. */
523 static char *
append_spec_date_format(const char * date_format,const char * spec_format)524 append_spec_date_format (const char *date_format, const char *spec_format) {
525   char *s = xmalloc (snprintf (NULL, 0, "%s%s", date_format, spec_format) + 1);
526   sprintf (s, "%s%s", date_format, spec_format);
527 
528   return s;
529 }
530 
531 /* Iterate over the given format and clean unwanted chars and keep all
532  * date/time specifiers such as %b%Y%d%M%S.
533  *
534  * On error NULL is returned.
535  * On success, a clean format containing only date/time specifiers is
536  * returned. */
537 static char *
clean_date_time_format(const char * format)538 clean_date_time_format (const char *format) {
539   char *fmt = NULL, *pr = NULL, *pw = NULL;
540   int special = 0;
541 
542   if (format == NULL || *format == '\0')
543     return NULL;
544 
545   fmt = xstrdup (format);
546   pr = fmt;
547   pw = fmt;
548   while (*pr) {
549     *pw = *pr++;
550     if (*pw == '%' || special) {
551       special = !special;
552       pw++;
553     }
554   }
555   *pw = '\0';
556 
557   return fmt;
558 }
559 
560 /* Determine if the given specifier character is an abbreviated type
561  * of date.
562  *
563  * If it is, 1 is returned, otherwise, 0 is returned. */
564 static int
is_date_abbreviated(const char * fdate)565 is_date_abbreviated (const char *fdate) {
566   if (strpbrk (fdate, "cDF"))
567     return 1;
568 
569   return 0;
570 }
571 
572 /* A wrapper to extract time specifiers from a time format.
573  *
574  * On error NULL is returned.
575  * On success, a clean format containing only time specifiers is
576  * returned. */
577 static char *
set_format_time(void)578 set_format_time (void) {
579   char *ftime = NULL;
580 
581   if (has_timestamp (conf.date_format) || !strcmp ("%T", conf.time_format))
582     ftime = xstrdup ("%H%M%S");
583   else
584     ftime = clean_date_time_format (conf.time_format);
585 
586   return ftime;
587 }
588 
589 /* A wrapper to extract date specifiers from a date format.
590  *
591  * On error NULL is returned.
592  * On success, a clean format containing only date specifiers is
593  * returned. */
594 static char *
set_format_date(void)595 set_format_date (void) {
596   char *fdate = NULL;
597 
598   if (has_timestamp (conf.date_format))
599     fdate = xstrdup ("%Y%m%d");
600   else
601     fdate = clean_date_time_format (conf.date_format);
602 
603   return fdate;
604 }
605 
606 /* Once we have a numeric date format, we attempt to read the time
607  * format and construct a date_time numeric specificity format (if any
608  * specificity is given). The result may look like Ymd[HM].
609  *
610  * On success, the numeric date time specificity format is set. */
611 static void
set_spec_date_time_num_format(void)612 set_spec_date_time_num_format (void) {
613   char *buf = NULL, *tf = set_format_time ();
614   const char *df = conf.date_num_format;
615 
616   if (!df || !tf) {
617     free (tf);
618     return;
619   }
620 
621   if (conf.date_spec_hr && strchr (tf, 'H'))
622     buf = append_spec_date_format (df, "%H");
623   else
624     buf = xstrdup (df);
625 
626   conf.spec_date_time_num_format = buf;
627   free (tf);
628 }
629 
630 /* Set a human readable specificity date and time format.
631  *
632  * On success, the human readable date time specificity format is set. */
633 static void
set_spec_date_time_format(void)634 set_spec_date_time_format (void) {
635   char *buf = NULL;
636   const char *fmt = conf.spec_date_time_num_format;
637   int buflen = 0, flen = 0;
638 
639   if (!fmt)
640     return;
641 
642   flen = (strlen (fmt) * 2) + 1;
643   buf = xcalloc (flen, sizeof (char));
644 
645   if (strchr (fmt, 'd'))
646     buflen += snprintf (buf + buflen, flen - buflen, "%%d/");
647   if (strchr (fmt, 'm'))
648     buflen += snprintf (buf + buflen, flen - buflen, "%%b/");
649   if (strchr (fmt, 'Y'))
650     buflen += snprintf (buf + buflen, flen - buflen, "%%Y");
651   if (strchr (fmt, 'H'))
652     buflen += snprintf (buf + buflen, flen - buflen, ":%%H");
653 
654   conf.spec_date_time_format = buf;
655 }
656 
657 /* Normalize the date format from the date format given by the user to
658  * Ymd so it can be sorted out properly afterwards.
659  *
660  * On error or unable to determine the format, 1 is returned.
661  * On success, the numeric date format as Ymd is set and 0 is
662  * returned. */
663 static int
set_date_num_format(void)664 set_date_num_format (void) {
665   char *fdate = NULL, *buf = NULL;
666   int buflen = 0, flen = 0;
667 
668   fdate = set_format_date ();
669   if (!fdate)
670     return 1;
671 
672   if (is_date_abbreviated (fdate)) {
673     free (fdate);
674     conf.date_num_format = xstrdup ("%Y%m%d");
675     return 0;
676   }
677 
678   flen = strlen (fdate) + 1;
679   flen = MAX (MIN_DATENUM_FMT_LEN, flen);       /* at least %Y%m%d + 1 */
680   buf = xcalloc (flen, sizeof (char));
681 
682   /* always add a %Y */
683   buflen += snprintf (buf + buflen, flen - buflen, "%%Y");
684   if (strpbrk (fdate, "hbmBf*"))
685     buflen += snprintf (buf + buflen, flen - buflen, "%%m");
686   if (strpbrk (fdate, "def*"))
687     buflen += snprintf (buf + buflen, flen - buflen, "%%d");
688 
689   conf.date_num_format = buf;
690   free (fdate);
691 
692   return buflen == 0 ? 1 : 0;
693 }
694 
695 /* Determine if we have a valid JSON format */
696 int
is_json_log_format(const char * fmt)697 is_json_log_format (const char *fmt) {
698   enum json_type t = JSON_ERROR;
699   json_stream json;
700 
701   json_open_string (&json, fmt);
702   /* ensure we use strict JSON when determining if we're using a JSON format */
703   json_set_streaming (&json, false);
704   do {
705     t = json_next (&json);
706     switch (t) {
707     case JSON_ERROR:
708       json_close (&json);
709       return 0;
710     default:
711       break;
712     }
713   } while (t != JSON_DONE && t != JSON_ERROR);
714   json_close (&json);
715 
716   return 1;
717 }
718 
719 /* Append the source string to destination and reallocates and
720  * updating the destination buffer appropriately. */
721 static void
ws_append_str(char ** dest,const char * src)722 ws_append_str (char **dest, const char *src) {
723   size_t curlen = strlen (*dest);
724   size_t srclen = strlen (src);
725   size_t newlen = curlen + srclen;
726 
727   char *str = xrealloc (*dest, newlen + 1);
728   memcpy (str + curlen, src, srclen + 1);
729   *dest = str;
730 }
731 
732 /* Delete the given key from a nested object key or empty the key. */
733 static void
dec_json_key(char * key)734 dec_json_key (char *key) {
735   char *ptr = NULL;
736   if (key && (ptr = strrchr (key, '.')))
737     *ptr = '\0';
738   else if (key && !(ptr = strrchr (key, '.')))
739     key[0] = '\0';
740 }
741 
742 /* Given a JSON string, parse it and call the given function pointer after each
743  * value.
744  *
745  * On error, a non-zero value is returned.
746  * On success, 0 is returned. */
747 int
parse_json_string(void * ptr_data,const char * str,int (* cb)(void *,char *,char *))748 parse_json_string (void *ptr_data, const char *str, int (*cb) (void *, char *, char *)) {
749   char *key = NULL, *val = NULL;
750   enum json_type ctx = JSON_ERROR, t = JSON_ERROR;
751   int ret = 0;
752   size_t len = 0, level = 0;
753   json_stream json;
754 
755   json_open_string (&json, str);
756   do {
757     t = json_next (&json);
758 
759     switch (t) {
760     case JSON_OBJECT:
761       if (key == NULL)
762         key = xstrdup ("");
763       break;
764     case JSON_ARRAY_END:
765     case JSON_OBJECT_END:
766       dec_json_key (key);
767       break;
768     case JSON_TRUE:
769       val = xstrdup ("true");
770       if ((ret = (*cb) (ptr_data, key, val)))
771         goto clean;
772       ctx = json_get_context (&json, &level);
773       if (ctx != JSON_ARRAY)
774         dec_json_key (key);
775       free (val);
776       val = NULL;
777       break;
778     case JSON_FALSE:
779       val = xstrdup ("false");
780       if ((ret = (*cb) (ptr_data, key, val)))
781         goto clean;
782       ctx = json_get_context (&json, &level);
783       if (ctx != JSON_ARRAY)
784         dec_json_key (key);
785       free (val);
786       val = NULL;
787       break;
788     case JSON_NULL:
789       val = xstrdup ("-");
790       if ((ret = (*cb) (ptr_data, key, val)))
791         goto clean;
792       ctx = json_get_context (&json, &level);
793       if (ctx != JSON_ARRAY)
794         dec_json_key (key);
795       free (val);
796       val = NULL;
797       break;
798     case JSON_STRING:
799     case JSON_NUMBER:
800       ctx = json_get_context (&json, &level);
801       /* key */
802       if ((level % 2) != 0 && ctx != JSON_ARRAY) {
803         if (strlen (key) != 0)
804           ws_append_str (&key, ".");
805         ws_append_str (&key, json_get_string (&json, &len));
806       }
807       /* val */
808       else if (key && (ctx == JSON_ARRAY || ((level % 2) == 0 && ctx != JSON_ARRAY))) {
809         val = xstrdup (json_get_string (&json, &len));
810         if ((ret = (*cb) (ptr_data, key, val)))
811           goto clean;
812         if (ctx != JSON_ARRAY)
813           dec_json_key (key);
814 
815         free (val);
816         val = NULL;
817       }
818       break;
819     case JSON_ERROR:
820       ret = -1;
821       goto clean;
822       break;
823     default:
824       break;
825     }
826   } while (t != JSON_DONE && t != JSON_ERROR);
827 
828 clean:
829   free (val);
830   free (key);
831   json_close (&json);
832 
833   return ret;
834 }
835 
836 /* If specificity is supplied, then determine which value we need to
837  * append to the date format. */
838 void
set_spec_date_format(void)839 set_spec_date_format (void) {
840   if (verify_formats ())
841     return;
842 
843   if (conf.is_json_log_format) {
844     if (parse_json_string (NULL, conf.log_format, ht_insert_json_logfmt) == -1)
845       FATAL ("Invalid JSON log format. Verify the syntax.");
846   }
847 
848   if (conf.date_num_format)
849     free (conf.date_num_format);
850   if (conf.spec_date_time_format)
851     free (conf.spec_date_time_format);
852   if (conf.spec_date_time_num_format)
853     free (conf.spec_date_time_num_format);
854 
855   if (set_date_num_format () == 0) {
856     set_spec_date_time_num_format ();
857     set_spec_date_time_format ();
858   }
859 }
860 
861 /* Attempt to set the date format given a command line option
862  * argument. The supplied optarg can be either an actual format string
863  * or the enumerated value such as VCOMBINED */
864 void
set_date_format_str(const char * oarg)865 set_date_format_str (const char *oarg) {
866   char *fmt = NULL;
867   int type = get_log_format_item_enum (oarg);
868 
869   /* free date format if it was previously set by set_log_format_str() */
870   if (conf.date_format)
871     free (conf.date_format);
872 
873   /* type not found, use whatever was given by the user then */
874   if (type == -1) {
875     conf.date_format = unescape_str (oarg);
876     return;
877   }
878 
879   /* attempt to get the format string by the enum value */
880   if ((fmt = get_selected_date_str (type)) == NULL) {
881     LOG_DEBUG (("Unable to set date format from enum: %s\n", oarg));
882     return;
883   }
884 
885   conf.date_format = fmt;
886 }
887 
888 /* Attempt to set the time format given a command line option
889  * argument. The supplied optarg can be either an actual format string
890  * or the enumerated value such as VCOMBINED */
891 void
set_time_format_str(const char * oarg)892 set_time_format_str (const char *oarg) {
893   char *fmt = NULL;
894   int type = get_log_format_item_enum (oarg);
895 
896   /* free time format if it was previously set by set_log_format_str() */
897   if (conf.time_format)
898     free (conf.time_format);
899 
900   /* type not found, use whatever was given by the user then */
901   if (type == -1) {
902     conf.time_format = unescape_str (oarg);
903     return;
904   }
905 
906   /* attempt to get the format string by the enum value */
907   if ((fmt = get_selected_time_str (type)) == NULL) {
908     LOG_DEBUG (("Unable to set time format from enum: %s\n", oarg));
909     return;
910   }
911 
912   conf.time_format = fmt;
913 }
914 
915 /* Determine if some global flags were set through log-format. */
916 static void
contains_specifier(void)917 contains_specifier (void) {
918   conf.serve_usecs = conf.bandwidth = 0;        /* flag */
919   if (!conf.log_format)
920     return;
921 
922   if (strstr (conf.log_format, "%b"))
923     conf.bandwidth = 1; /* flag */
924   if (strstr (conf.log_format, "%D"))
925     conf.serve_usecs = 1;       /* flag */
926   if (strstr (conf.log_format, "%T"))
927     conf.serve_usecs = 1;       /* flag */
928   if (strstr (conf.log_format, "%L"))
929     conf.serve_usecs = 1;       /* flag */
930 }
931 
932 /* Attempt to set the log format given a command line option argument.
933  * The supplied optarg can be either an actual format string or the
934  * enumerated value such as VCOMBINED */
935 void
set_log_format_str(const char * oarg)936 set_log_format_str (const char *oarg) {
937   char *fmt = NULL;
938   int type = get_log_format_item_enum (oarg);
939 
940   /* free log format if it was previously set */
941   if (conf.log_format)
942     free (conf.log_format);
943 
944   if (type == -1 && is_json_log_format (oarg)) {
945     conf.is_json_log_format = 1;
946     conf.log_format = unescape_str (oarg);
947     contains_specifier ();      /* set flag */
948     return;
949   }
950 
951   /* type not found, use whatever was given by the user then */
952   if (type == -1) {
953     conf.log_format = unescape_str (oarg);
954     contains_specifier ();      /* set flag */
955     return;
956   }
957 
958   /* attempt to get the format string by the enum value */
959   if ((fmt = get_selected_format_str (type)) == NULL) {
960     LOG_DEBUG (("Unable to set log format from enum: %s\n", oarg));
961     return;
962   }
963 
964   if (is_json_log_format (fmt))
965     conf.is_json_log_format = 1;
966 
967   conf.log_format = unescape_str (fmt);
968   contains_specifier ();        /* set flag */
969 
970   /* assume we are using the default date/time formats */
971   set_time_format_str (oarg);
972   set_date_format_str (oarg);
973   free (fmt);
974 }
975