1 /**
2  * commons.c -- holds different data types
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 #if HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <time.h>
39 #include <unistd.h>
40 
41 #include "commons.h"
42 
43 #include "error.h"
44 #include "labels.h"
45 #include "settings.h"
46 #include "util.h"
47 #include "xmalloc.h"
48 
49 /* processing time */
50 time_t end_proc;
51 time_t timestamp;
52 time_t start_proc;
53 
54 /* list of available modules/panels */
55 int module_list[TOTAL_MODULES] = {[0 ... TOTAL_MODULES - 1] = -1 };
56 
57 /* *INDENT-OFF* */
58 /* String modules to enumerated modules */
59 static GEnum enum_modules[] = {
60   {"VISITORS"        , VISITORS}        ,
61   {"REQUESTS"        , REQUESTS}        ,
62   {"REQUESTS_STATIC" , REQUESTS_STATIC} ,
63   {"NOT_FOUND"       , NOT_FOUND}       ,
64   {"HOSTS"           , HOSTS}           ,
65   {"OS"              , OS}              ,
66   {"BROWSERS"        , BROWSERS}        ,
67   {"VISIT_TIMES"     , VISIT_TIMES}     ,
68   {"VIRTUAL_HOSTS"   , VIRTUAL_HOSTS}   ,
69   {"REFERRERS"       , REFERRERS}       ,
70   {"REFERRING_SITES" , REFERRING_SITES} ,
71   {"KEYPHRASES"      , KEYPHRASES}      ,
72   {"STATUS_CODES"    , STATUS_CODES}    ,
73   {"REMOTE_USER"     , REMOTE_USER}     ,
74   {"CACHE_STATUS"    , CACHE_STATUS}    ,
75 #ifdef HAVE_GEOLOCATION
76   {"GEO_LOCATION"    , GEO_LOCATION}    ,
77 #endif
78   {"MIME_TYPE"       , MIME_TYPE}       ,
79   {"TLS_TYPE"        , TLS_TYPE}        ,
80 };
81 /* *INDENT-ON* */
82 
83 /* Get number of items per panel to parse.
84  *
85  * The number of items per panel is returned. */
86 int
get_max_choices(void)87 get_max_choices (void) {
88   char *csv = NULL, *json = NULL, *html = NULL;
89   int max = MAX_CHOICES;
90 
91   /* no max choices, return defaults */
92   if (conf.max_items <= 0)
93     return conf.real_time_html ? MAX_CHOICES_RT : MAX_CHOICES;
94 
95   /* TERM */
96   if (!conf.output_stdout)
97     return conf.max_items > MAX_CHOICES ? MAX_CHOICES : conf.max_items;
98 
99   /* REAL-TIME STDOUT */
100   /* real time HTML, display max rt choices */
101   if (conf.real_time_html)
102     return conf.max_items > MAX_CHOICES_RT ? MAX_CHOICES_RT : conf.max_items;
103 
104   /* STDOUT */
105   /* CSV - allow n amount of choices */
106   if (find_output_type (&csv, "csv", 1) == 0)
107     max = conf.max_items;
108   /* JSON - allow n amount of choices */
109   if (find_output_type (&json, "json", 1) == 0 && conf.max_items > 0)
110     max = conf.max_items;
111   /* HTML - takes priority on cases where multiple outputs were given. Note that
112    * we check either for an .html extension or we assume not extension was passed
113    * via -o and therefore we are redirecting the output to a file. */
114   if (find_output_type (&html, "html", 1) == 0 || conf.output_format_idx == 0)
115     max = conf.max_items;
116 
117   free (csv);
118   free (html);
119   free (json);
120 
121   return max;
122 }
123 
124 /* Calculate a percentage.
125  *
126  * The percentage is returned. */
127 float
get_percentage(unsigned long long total,unsigned long long hit)128 get_percentage (unsigned long long total, unsigned long long hit) {
129   return (total == 0 ? 0 : (((float) hit) / total) * 100);
130 }
131 
132 /* Display the storage being used. */
133 void
display_storage(void)134 display_storage (void) {
135   fprintf (stdout, "%s\n", BUILT_WITH_DEFHASH);
136 }
137 
138 /* Display the path of the default configuration file when `-p` is not used */
139 void
display_default_config_file(void)140 display_default_config_file (void) {
141   char *path = get_config_file_path ();
142 
143   if (!path) {
144     fprintf (stdout, "%s\n", ERR_NODEF_CONF_FILE);
145     fprintf (stdout, "%s `-p /path/goaccess.conf`\n", ERR_NODEF_CONF_FILE_DESC);
146   } else {
147     fprintf (stdout, "%s\n", path);
148     free (path);
149   }
150 }
151 
152 /* Display the current version. */
153 void
display_version(void)154 display_version (void) {
155   fprintf (stdout, "GoAccess - %s.\n", GO_VERSION);
156   fprintf (stdout, "%s: %s\n", INFO_MORE_INFO, GO_WEBSITE);
157   fprintf (stdout, "Copyright (C) 2009-2020 by Gerardo Orellana\n");
158   fprintf (stdout, "\nBuild configure arguments:\n");
159 #ifdef DEBUG
160   fprintf (stdout, "  --enable-debug\n");
161 #endif
162 #ifdef HAVE_NCURSESW_NCURSES_H
163   fprintf (stdout, "  --enable-utf8\n");
164 #endif
165 #ifdef HAVE_LIBGEOIP
166   fprintf (stdout, "  --enable-geoip=legacy\n");
167 #endif
168 #ifdef HAVE_LIBMAXMINDDB
169   fprintf (stdout, "  --enable-geoip=mmdb\n");
170 #endif
171 #ifdef WITH_GETLINE
172   fprintf (stdout, "  --with-getline\n");
173 #endif
174 #ifdef HAVE_LIBSSL
175   fprintf (stdout, "  --with-openssl\n");
176 #endif
177 }
178 
179 /* Get the enumerated value given a string.
180  *
181  * On error, -1 is returned.
182  * On success, the enumerated module value is returned. */
183 int
str2enum(const GEnum map[],int len,const char * str)184 str2enum (const GEnum map[], int len, const char *str) {
185   int i;
186 
187   for (i = 0; i < len; ++i) {
188     if (!strcmp (str, map[i].str))
189       return map[i].idx;
190   }
191 
192   return -1;
193 }
194 
195 /* Get the string value given an enum.
196  *
197  * On error, -1 is returned.
198  * On success, the enumerated module value is returned. */
199 char *
enum2str(const GEnum map[],int len,int idx)200 enum2str (const GEnum map[], int len, int idx) {
201   int i;
202 
203   for (i = 0; i < len; ++i) {
204     if (idx == map[i].idx)
205       return xstrdup (map[i].str);
206   }
207 
208   return NULL;
209 }
210 
211 /* Get the enumerated module value given a module string.
212  *
213  * On error, -1 is returned.
214  * On success, the enumerated module value is returned. */
215 int
get_module_enum(const char * str)216 get_module_enum (const char *str) {
217   return str2enum (enum_modules, ARRAY_SIZE (enum_modules), str);
218 }
219 
220 /* Get the module string value given a module enum value.
221  *
222  * On error, NULL is returned.
223  * On success, the string module value is returned. */
224 char *
get_module_str(GModule module)225 get_module_str (GModule module) {
226   return enum2str (enum_modules, ARRAY_SIZE (enum_modules), module);
227 }
228 
229 /* Instantiate a new GAgents structure.
230  *
231  * On success, the newly malloc'd structure is returned. */
232 GAgents *
new_gagents(uint32_t size)233 new_gagents (uint32_t size) {
234   GAgents *agents = xmalloc (sizeof (GAgents));
235   memset (agents, 0, sizeof *agents);
236 
237   agents->items = xcalloc (size, sizeof (GAgentItem));
238   agents->size = size;
239   agents->idx = 0;
240 
241   return agents;
242 }
243 
244 /* Clean the array of agents. */
245 void
free_agents_array(GAgents * agents)246 free_agents_array (GAgents * agents) {
247   int i;
248 
249   if (agents == NULL)
250     return;
251 
252   /* clean stuff up */
253   for (i = 0; i < agents->idx; ++i)
254     free (agents->items[i].agent);
255   if (agents->items)
256     free (agents->items);
257   free (agents);
258 }
259 
260 /* Determine if the given date format is a timestamp.
261  *
262  * If not a timestamp, 0 is returned.
263  * If it is a timestamp, 1 is returned. */
264 int
has_timestamp(const char * fmt)265 has_timestamp (const char *fmt) {
266   if (strcmp ("%s", fmt) == 0 || strcmp ("%f", fmt) == 0)
267     return 1;
268   return 0;
269 }
270 
271 /* Determine if the given module is set to be enabled.
272  *
273  * If enabled, 1 is returned, else 0 is returned. */
274 int
enable_panel(GModule mod)275 enable_panel (GModule mod) {
276   int i, module;
277 
278   for (i = 0; i < conf.enable_panel_idx; ++i) {
279     if ((module = get_module_enum (conf.enable_panels[i])) == -1)
280       continue;
281     if (mod == (unsigned int) module) {
282       return 1;
283     }
284   }
285 
286   return 0;
287 }
288 
289 /* Determine if the given module is set to be ignored.
290  *
291  * If ignored, 1 is returned, else 0 is returned. */
292 int
ignore_panel(GModule mod)293 ignore_panel (GModule mod) {
294   int i, module;
295 
296   for (i = 0; i < conf.ignore_panel_idx; ++i) {
297     if ((module = get_module_enum (conf.ignore_panels[i])) == -1)
298       continue;
299     if (mod == (unsigned int) module) {
300       return 1;
301     }
302   }
303 
304   return 0;
305 }
306 
307 /* Get the number of available modules/panels.
308  *
309  * The number of modules available is returned. */
310 uint32_t
get_num_modules(void)311 get_num_modules (void) {
312   size_t idx = 0;
313   uint32_t num = 0;
314 
315   FOREACH_MODULE (idx, module_list) {
316     num++;
317   }
318 
319   return num;
320 }
321 
322 /* Get the index from the module_list given a module.
323  *
324  * If the module is not within the array, -1 is returned.
325  * If the module is within the array, the index is returned. */
326 int
get_module_index(int module)327 get_module_index (int module) {
328   size_t idx = 0;
329 
330   FOREACH_MODULE (idx, module_list) {
331     if (module_list[idx] == module)
332       return idx;
333   }
334 
335   return -1;
336 }
337 
338 /* Remove the given module from the module_list array.
339  *
340  * If the module is not within the array, 1 is returned.
341  * If the module is within the array, it is removed from the array and
342  * 0 is returned. */
343 int
remove_module(GModule module)344 remove_module (GModule module) {
345   int idx = get_module_index (module);
346   if (idx == -1)
347     return 1;
348 
349   if (idx < TOTAL_MODULES - 1)
350     memmove (&module_list[idx], &module_list[idx + 1],
351              ((TOTAL_MODULES - 1) - idx) * sizeof (module_list[0]));
352   module_list[TOTAL_MODULES - 1] = -1;
353 
354   return 0;
355 }
356 
357 /* Find the next module given the current module.
358  *
359  * The next available module in the array is returned. */
360 int
get_next_module(GModule module)361 get_next_module (GModule module) {
362   int next = get_module_index (module) + 1;
363 
364   if (next == TOTAL_MODULES || module_list[next] == -1)
365     return module_list[0];
366 
367   return module_list[next];
368 }
369 
370 /* Find the previous module given the current module.
371  *
372  * The previous available module in the array is returned. */
373 int
get_prev_module(GModule module)374 get_prev_module (GModule module) {
375   int i;
376   int next = get_module_index (module) - 1;
377 
378   if (next >= 0 && module_list[next] != -1)
379     return module_list[next];
380 
381   for (i = TOTAL_MODULES - 1; i >= 0; i--) {
382     if (module_list[i] != -1) {
383       return module_list[i];
384     }
385   }
386 
387   return 0;
388 }
389 
390 /* Perform some additional tasks to panels before they are being
391  * parsed.
392  *
393  * Note: This overwrites --enable-panel since it assumes there's
394  * truly nothing to do with the panel */
395 void
verify_panels(void)396 verify_panels (void) {
397   int ignore_panel_idx = conf.ignore_panel_idx;
398 
399   /* Remove virtual host panel if no '%v' within log format */
400   if (!conf.log_format)
401     return;
402 
403   if (!strstr (conf.log_format, "%v") && ignore_panel_idx < TOTAL_MODULES) {
404     if (str_inarray ("VIRTUAL_HOSTS", conf.ignore_panels, ignore_panel_idx) < 0)
405       remove_module (VIRTUAL_HOSTS);
406   }
407   if (!strstr (conf.log_format, "%e") && ignore_panel_idx < TOTAL_MODULES) {
408     if (str_inarray ("REMOTE_USER", conf.ignore_panels, ignore_panel_idx) < 0)
409       remove_module (REMOTE_USER);
410   }
411   if (!strstr (conf.log_format, "%C") && ignore_panel_idx < TOTAL_MODULES) {
412     if (str_inarray ("CACHE_STATUS", conf.ignore_panels, ignore_panel_idx) < 0)
413       remove_module (CACHE_STATUS);
414   }
415   if (!strstr (conf.log_format, "%M") && ignore_panel_idx < TOTAL_MODULES) {
416     if (str_inarray ("MIME_TYPE", conf.ignore_panels, ignore_panel_idx) < 0)
417       remove_module (MIME_TYPE);
418   }
419   if (!strstr (conf.log_format, "%K") && ignore_panel_idx < TOTAL_MODULES) {
420     if (str_inarray ("TLS_TYPE", conf.ignore_panels, ignore_panel_idx) < 0)
421       remove_module (TLS_TYPE);
422   }
423 #ifdef HAVE_GEOLOCATION
424 #ifdef HAVE_LIBMAXMINDDB
425   if (!conf.geoip_database && ignore_panel_idx < TOTAL_MODULES) {
426     if (str_inarray ("GEO_LOCATION", conf.ignore_panels, ignore_panel_idx) < 0)
427       remove_module (GEO_LOCATION);
428   }
429 #endif
430 #endif
431 }
432 
433 /* Build an array of available modules (ignores listed panels).
434  *
435  * If there are no modules enabled, 0 is returned.
436  * On success, the first enabled module is returned. */
437 int
init_modules(void)438 init_modules (void) {
439   GModule module;
440   int i;
441 
442   /* init - terminating with -1 */
443   for (module = 0; module < TOTAL_MODULES; ++module)
444     module_list[module] = -1;
445 
446   for (i = 0, module = 0; module < TOTAL_MODULES; ++module) {
447     if (!ignore_panel (module) || enable_panel (module)) {
448       module_list[i++] = module;
449     }
450   }
451 
452   return module_list[0] > -1 ? module_list[0] : 0;
453 }
454 
455 /* Get the logs size.
456  *
457  * If log was piped (from stdin), 0 is returned.
458  * On success, it adds up all log sizes and its value is returned.
459  * if --log-size was specified, it will be returned explicitly */
460 intmax_t
get_log_sizes(void)461 get_log_sizes (void) {
462   int i;
463   off_t size = 0;
464 
465   /* --log-size */
466   if (conf.log_size > 0)
467     return (intmax_t) conf.log_size;
468 
469   for (i = 0; i < conf.filenames_idx; ++i) {
470     if (conf.filenames[i][0] == '-' && conf.filenames[i][1] == '\0')
471       size += 0;
472     else
473       size += file_size (conf.filenames[i]);
474   }
475 
476   return (intmax_t) size;
477 }
478 
479 /* Get the log sources used.
480  *
481  * On success, a newly malloc'd string containing the log source either
482  * from filename(s) and/or STDIN is returned. */
483 char *
get_log_source_str(int max_len)484 get_log_source_str (int max_len) {
485   char *str = xstrdup ("");
486   int i, len = 0;
487 
488   for (i = 0; i < conf.filenames_idx; ++i) {
489     if (conf.filenames[i][0] == '-' && conf.filenames[i][1] == '\0')
490       append_str (&str, "STDIN");
491     else
492       append_str (&str, conf.filenames[i]);
493     if (i != conf.filenames_idx - 1)
494       append_str (&str, "; ");
495   }
496 
497   len = strlen (str);
498   if (max_len > 0 && len > 0 && len > max_len) {
499     str[max_len - 3] = 0;
500     append_str (&str, "...");
501   }
502 
503   return str;
504 }
505