1 /*
2 * SARG Squid Analysis Report Generator http://sarg.sourceforge.net
3 * 1998, 2015
4 *
5 * SARG donations:
6 * please look at http://sarg.sourceforge.net/donations.php
7 * Support:
8 * http://sourceforge.net/projects/sarg/forums/forum/363374
9 * ---------------------------------------------------------------------
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
24 *
25 */
26
27 // #define LEGACY_MY_ATOLL
28 // #define LEGACY_TESTVALIDUSERCHAR
29
30 #include <sys/param.h>
31 #include <sys/stat.h>
32
33 #include "include/conf.h"
34 #include "include/defs.h"
35
36 #if defined(__MINGW32__) && defined(HAVE_DIRECT_H)
37 #define NO_OLDNAMES 1
38 #include <direct.h>
39 #endif
40
41 #if defined(HAVE_BACKTRACE)
42 #define USE_GETWORD_BACKTRACE 1
43 #else
44 #define USE_GETWORD_BACKTRACE 0
45 #endif
46
47 static char mtab1[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
48
49 //! The list of the HTTP codes to exclude from the report.
50 static char *excludecode=NULL;
51
52 //! Directory where the images are stored.
53 char ImageDir[MAXLEN]=IMAGEDIR;
54
55 extern char *CurrentLocale;
56
57 #if USE_GETWORD_BACKTRACE
getword_backtrace(void)58 static void getword_backtrace(void)
59 {
60 void *buffer[5];
61 int i, n;
62 char **calls;
63
64 n=backtrace(buffer,sizeof(buffer)/sizeof(buffer[0]));
65 if (n<=0) return;
66 calls=backtrace_symbols(buffer,n);
67 if (calls) {
68 debuga(__FILE__,__LINE__,_("getword backtrace:\n"));
69 for (i=0 ; i<n ; i++) {
70 fprintf(stderr,"SARG: %d:%s\n",i+1,calls[i]);
71 }
72 free(calls);
73 }
74 }
75 #endif //USE_GETWORD_BACKTRACE
76
getword_start(struct getwordstruct * gwarea,const char * line)77 void getword_start(struct getwordstruct *gwarea, const char *line)
78 {
79 gwarea->beginning=line;
80 gwarea->current=line;
81 gwarea->modified=0;
82 }
83
getword_restart(struct getwordstruct * gwarea)84 void getword_restart(struct getwordstruct *gwarea)
85 {
86 if (gwarea->modified) {
87 debuga(__FILE__,__LINE__,_("Cannot parse again the line as it was modified\n"));
88 exit(EXIT_FAILURE);
89 }
90 gwarea->current=gwarea->beginning;
91 }
92
getword(char * word,int limit,struct getwordstruct * gwarea,char stop)93 int getword(char *word, int limit, struct getwordstruct *gwarea, char stop)
94 {
95 int x;
96
97 for (x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++) {
98 if (x>=limit) {
99 /*
100 TRANSLATORS: The %s is the name of the function reporting the
101 error message.
102 */
103 debuga(__FILE__,__LINE__,_("End of word not found in %s after %d bytes.\n"),__func__,x);
104 debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
105 debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
106 debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
107 word[(limit>0) ? limit-1 : 0]='\0';
108 #if USE_GETWORD_BACKTRACE
109 getword_backtrace();
110 #endif
111 return(-1);
112 }
113 word[x] = gwarea->current[x];
114 }
115
116 word[x] = '\0';
117 if (gwarea->current[x]) ++x;
118 gwarea->current+=x;
119 return(0);
120 }
121
getword_limit(char * word,int limit,struct getwordstruct * gwarea,char stop)122 int getword_limit(char *word, int limit, struct getwordstruct *gwarea, char stop)
123 {
124 int x;
125
126 limit--;
127 for (x=0; x<limit && gwarea->current[x] && gwarea->current[x] != stop ;x++) {
128 word[x] = gwarea->current[x];
129 }
130 word[x] = '\0';
131 gwarea->current+=x;
132 while (*gwarea->current && *gwarea->current != stop) gwarea->current++;
133 if (*gwarea->current) ++gwarea->current;
134 return(0);
135 }
136
getword_multisep(char * word,int limit,struct getwordstruct * gwarea,char stop)137 int getword_multisep(char *word, int limit, struct getwordstruct *gwarea, char stop)
138 {
139 int x;
140
141 for (x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++) {
142 if (x>=limit) {
143 debuga(__FILE__,__LINE__,_("End of word not found in %s after %d bytes.\n"),__func__,x);
144 debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
145 debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
146 debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
147 if (limit>0) word[limit-1]='\0';
148 #if USE_GETWORD_BACKTRACE
149 getword_backtrace();
150 #endif
151 //exit(EXIT_FAILURE);
152 return(-1);
153 }
154 word[x] = gwarea->current[x];
155 }
156
157 word[x] = '\0';
158 while (gwarea->current[x] && gwarea->current[x]==stop) ++x;
159 gwarea->current+=x;
160 return(0);
161 }
162
getword_skip(int limit,struct getwordstruct * gwarea,char stop)163 int getword_skip(int limit, struct getwordstruct *gwarea, char stop)
164 {
165 int x;
166
167 for (x=0;(gwarea->current[x] && (gwarea->current[x] != stop ));x++) {
168 if (x>=limit) {
169 debuga(__FILE__,__LINE__,_("End of word not found in %s after %d bytes.\n"),__func__,x);
170 debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
171 debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
172 debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
173 #if USE_GETWORD_BACKTRACE
174 getword_backtrace();
175 #endif
176 return(-1);
177 }
178 }
179
180 if (gwarea->current[x]) ++x;
181 gwarea->current+=x;
182 return(0);
183 }
184
getword_atoll(long long int * number,struct getwordstruct * gwarea,char stop)185 int getword_atoll(long long int *number, struct getwordstruct *gwarea, char stop)
186 {
187 int x;
188 int sign=+1;
189 int digit;
190
191 if (gwarea->current[0] == '-') {
192 gwarea->current++;
193 sign=-1;
194 } else if (gwarea->current[0] == '+') {
195 gwarea->current++;
196 }
197 *number=0LL;
198 for (x=0;isdigit(gwarea->current[x]);x++) {
199 digit=gwarea->current[x]-'0';
200 if (*number >= (LLONG_MAX-digit)/10) {
201 /*
202 TRANSLATORS: The first %s is the function name (in the source code) where the
203 overflow is detected.
204 */
205 debuga(__FILE__,__LINE__,_("Integer overflow detected in %s in line %s\n"),__func__,gwarea->beginning);
206 return(-1);
207 }
208 *number=(*number * 10) + digit;
209 }
210 if (gwarea->current[x] && gwarea->current[x]!=stop) {
211 /*
212 TRANSLATORS: The %s is the function name, in the source code, where the problem occured.
213 */
214 debuga(__FILE__,__LINE__,_("End of number not found in %s after %d bytes.\n"),__func__,x);
215 debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
216 debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
217 debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
218 #if USE_GETWORD_BACKTRACE
219 getword_backtrace();
220 #endif
221 return(-1);
222 }
223 *number*=sign;
224
225 if (gwarea->current[x]) ++x;
226 gwarea->current+=x;
227 return(0);
228 }
229
getword_atoi(int * number,struct getwordstruct * gwarea,char stop)230 int getword_atoi(int *number, struct getwordstruct *gwarea, char stop)
231 {
232 int x;
233 int sign=+1;
234 int digit;
235
236 if (gwarea->current[0] == '-') {
237 gwarea->current++;
238 sign=-1;
239 } else if (gwarea->current[0] == '+') {
240 gwarea->current++;
241 }
242 *number=0;
243 for (x=0;isdigit(gwarea->current[x]);x++) {
244 digit=gwarea->current[x]-'0';
245 if (*number > (INT_MAX-digit)/10) {
246 debuga(__FILE__,__LINE__,_("Integer overflow detected in %s in line %s\n"),__func__,gwarea->beginning);
247 return(-1);
248 }
249 *number=(*number * 10) + digit;
250 }
251 if (gwarea->current[x] && gwarea->current[x]!=stop) {
252 debuga(__FILE__,__LINE__,_("End of number not found in %s after %d bytes.\n"),__func__,x);
253 debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
254 debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
255 debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
256 #if USE_GETWORD_BACKTRACE
257 getword_backtrace();
258 #endif
259 return(-1);
260 }
261 *number*=sign;
262
263 if (gwarea->current[x]) ++x;
264 gwarea->current+=x;
265 return(0);
266 }
267
getword_atol(long int * number,struct getwordstruct * gwarea,char stop)268 int getword_atol(long int *number, struct getwordstruct *gwarea, char stop)
269 {
270 int x;
271 long int sign=+1;
272 int digit;
273
274 if (gwarea->current[0] == '-') {
275 gwarea->current++;
276 sign=-1;
277 } else if (gwarea->current[0] == '+') {
278 gwarea->current++;
279 }
280 *number=0;
281 for (x=0;isdigit(gwarea->current[x]);x++) {
282 digit=gwarea->current[x]-'0';
283 if (*number > (LONG_MAX-digit)/10) {
284 debuga(__FILE__,__LINE__,_("Integer overflow detected in %s in line %s\n"),__func__,gwarea->beginning);
285 return(-1);
286 }
287 *number=(*number * 10) + digit;
288 }
289 if (gwarea->current[x] && gwarea->current[x]!=stop) {
290 debuga(__FILE__,__LINE__,_("End of number not found in %s after %d bytes.\n"),__func__,x);
291 debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
292 debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
293 debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
294 #if USE_GETWORD_BACKTRACE
295 getword_backtrace();
296 #endif
297 return(-1);
298 }
299 *number*=sign;
300
301 if (gwarea->current[x]) ++x;
302 gwarea->current+=x;
303 return(0);
304 }
305
getword_atolu(unsigned long int * number,struct getwordstruct * gwarea,char stop)306 int getword_atolu(unsigned long int *number, struct getwordstruct *gwarea, char stop)
307 {
308 int x;
309 int digit;
310
311 if (gwarea->current[0] == '-') {
312 debuga(__FILE__,__LINE__,_("getword_atolu got a negative number.\n"));
313 debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
314 debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
315 return(-1);
316 }
317 if (gwarea->current[0] == '+') {
318 gwarea->current++;
319 }
320 *number=0;
321 for (x=0;isdigit(gwarea->current[x]);x++) {
322 digit=gwarea->current[x]-'0';
323 if (*number > (ULONG_MAX-digit)/10) {
324 debuga(__FILE__,__LINE__,_("Integer overflow detected in %s in line %s\n"),__func__,gwarea->beginning);
325 return(-1);
326 }
327 *number=(*number * 10) + digit;
328 }
329 if (gwarea->current[x] && gwarea->current[x]!=stop) {
330 debuga(__FILE__,__LINE__,_("End of number not found in %s after %d bytes.\n"),__func__,x);
331 debuga(__FILE__,__LINE__,_("Line=\"%s\"\n"),gwarea->beginning);
332 debuga(__FILE__,__LINE__,_("Record=\"%s\"\n"),gwarea->current);
333 debuga(__FILE__,__LINE__,_("searching for \'x%x\'\n"),stop);
334 #if USE_GETWORD_BACKTRACE
335 getword_backtrace();
336 #endif
337 return(-1);
338 }
339
340 if (gwarea->current[x]) ++x;
341 gwarea->current+=x;
342 return(0);
343 }
344
345
getword_ptr(char * orig_line,char ** word,struct getwordstruct * gwarea,char stop)346 int getword_ptr(char *orig_line,char **word, struct getwordstruct *gwarea, char stop)
347 {
348 /*!
349 \note Why pass the original buffer to the function ? Because we must modify it to
350 insert the terminating ASCII zero for the word we return and that's not compatible
351 with getword_restart(). Moreover, getword_start() sometime works on constant strings
352 so this function require the original buffer to detect any missuse.
353 */
354 int x;
355 int sep;
356 int start;
357
358 if (orig_line && orig_line!=gwarea->beginning) {
359 debuga(__FILE__,__LINE__,_("Invalid buffer passed to getword_ptr\n"));
360 return(-1);
361 }
362
363 start=(gwarea->current-gwarea->beginning);
364 if (word && orig_line) *word=orig_line+start;
365 for (x=0;((gwarea->current[x]) && (gwarea->current[x] != stop ));x++);
366 sep=(gwarea->current[x]!='\0');
367 if (word && orig_line) orig_line[start+x] = '\0';
368 if (sep) ++x;
369 gwarea->current+=x;
370 gwarea->modified=1;
371 return(0);
372 }
373
374 #define MAXLLL 30 //!< Maximum number of digits in long long (a guess).
my_atoll(const char * nptr)375 long long int my_atoll (const char *nptr)
376 {
377 long long int returnval=0LL;
378 int max_digits = MAXLLL ;
379
380 // Soak up all the white space
381 while (isspace( *nptr )) {
382 nptr++;
383 }
384
385 //For each character left to right
386 //change the character to a single digit
387 //multiply what we had before by 10 and add the new digit
388
389 while (--max_digits && isdigit( *nptr ))
390 {
391 returnval = ( returnval * 10 ) + ( *nptr++ - '0' ) ;
392 }
393
394 return returnval;
395 }
396
is_absolute(const char * path)397 int is_absolute(const char *path)
398 {
399 if (*path=='/') return(1);
400 #ifdef _WIN32
401 if (isalpha(path[0]) && path[1]==':') return(1);
402 #endif
403 return(0);
404 }
405
PortableMkDir(const char * path,int mode)406 int PortableMkDir(const char *path,int mode)
407 {
408 #if defined(__linux__) || defined(__FreeBSD__)
409 int mkerror=mkdir(path,mode);
410 #else //mingw
411 (void)mode;
412 int mkerror=_mkdir(path);
413 #endif
414 return(mkerror);
415 }
416
417 /*!
418 * Recursively create a path by adding missing directory until the whole path is created.
419 * \param name The path to create.
420 * \return True if the directory was created or false if it already existed
421 */
my_mkdir(const char * name)422 bool my_mkdir(const char *name)
423 {
424 char w0[MAXLEN];
425 int i;
426 int chars;
427 bool created = false;
428 struct stat st;
429
430 if (!is_absolute(name)) {
431 debuga(__FILE__,__LINE__,_("Invalid path (%s). Please, use absolute paths only.\n"),name);
432 exit(EXIT_FAILURE);
433 }
434
435 chars=0;
436 for (i=0 ; name[i] ; i++) {
437 if (i>=sizeof(w0)) {
438 debuga(__FILE__,__LINE__,_("Path too long: "));
439 debuga_more("%s\n",name);
440 exit(EXIT_FAILURE);
441 }
442 if (chars>0 && name[i] == '/') {
443 w0[i] = '\0';
444 if (access(w0, R_OK) != 0) {
445 if (PortableMkDir(w0,0755)) {
446 debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),w0,strerror(errno));
447 exit(EXIT_FAILURE);
448 }
449 }
450 }
451 if (name[i] != '/') chars++;
452 w0[i] = name[i];
453 }
454
455 if (access(name, R_OK) != 0) {
456 if (PortableMkDir(name,0755)) {
457 debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),name,strerror(errno));
458 exit(EXIT_FAILURE);
459 }
460 created = true;
461 }
462 if (!created) {
463 /*
464 * Check the final path is a directory (symlink to a directory is ok).
465 */
466 if (stat(name, &st)) {
467 debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"), name, strerror(errno));
468 exit(EXIT_FAILURE);
469 }
470 if (!S_ISDIR(st.st_mode)) {
471 debuga(__FILE__,__LINE__,_("Directory \"%s\" can't be created because the path already exists and is not a directory\n"), name);
472 exit(EXIT_FAILURE);
473 }
474 }
475 return created;
476 }
477
makeTmpDir(const char * tmp)478 void makeTmpDir(const char *tmp)
479 {
480 /*
481 * We must ensure the temporary directory is ours. In particular, we must make sure no malicious
482 * users managed to create or replace the temporary directory with a symlink to a system directory.
483 * As sarg purges the content of the temporary directory upon exit, should the temporary directory
484 * be hijacked, sarg could be tricked in deleting system files such as /bin or users files in /home
485 * or logs in /var/log.
486 *
487 * The code first create the temporary directory. If it wasn't created, the content is checked and
488 * purged if it looks safe to delete every file and directory it contains.
489 */
490 if (!my_mkdir(tmp)) {
491 if (debug) debuga(__FILE__, __LINE__, _("Purging temporary directory \"%s\"\n"), tmp);
492 emptytmpdir(tmp);
493 }
494 }
495
my_lltoa(unsigned long long int n,char * s,int ssize,int len)496 void my_lltoa(unsigned long long int n, char *s, int ssize, int len)
497 {
498 int i;
499 int slen = 0;
500 int j;
501 char c;
502
503 ssize--;
504 if (len>ssize) {
505 debuga(__FILE__,__LINE__,_("The requested number of digits passed to my_lltoa (%d) is bigger than the output buffer size (%d)\n"),len,ssize);
506 abort();
507 }
508
509 do {
510 s[slen++] = (n % 10) + '0';
511 } while ((n /= 10) > 0 && slen<ssize);
512 s[slen] = '\0';
513
514 for (i = 0, j = slen-1; i<j; i++, j--) {
515 c = s[i];
516 s[i] = s[j];
517 s[j] = c;
518 }
519
520 if (len>slen) {
521 i=len-slen;
522 for (j=slen; j>=0; j--)
523 s[j+i]=s[j];
524 for (j=0 ; j<i ; j++)
525 s[j]='0';
526 }
527 }
528
month2num(const char * month)529 int month2num(const char *month)
530 {
531 int m;
532
533 for (m=0 ; m<12 && strcmp(mtab1[m],month) != 0; m++);
534 return(m);
535 }
536
builddia(int day,int month,int year)537 int builddia(int day, int month, int year)
538 {
539 return(year*10000+month*100+day);
540 }
541
542 /*!
543 Compare two dates.
544
545 \param date1 The first date to compare.
546 \param date2 The second date to compare.
547
548 \retval -1 If date1<date2.
549 \retval 0 If date1==date2.
550 \retval 1 if date1>date2.
551 */
compare_date(const struct tm * date1,const struct tm * date2)552 int compare_date(const struct tm *date1,const struct tm *date2)
553 {
554 if (date1->tm_year<date2->tm_year) return(-1);
555 if (date1->tm_year>date2->tm_year) return(1);
556 if (date1->tm_mon<date2->tm_mon) return(-1);
557 if (date1->tm_mon>date2->tm_mon) return(1);
558 if (date1->tm_mday<date2->tm_mday) return(-1);
559 if (date1->tm_mday>date2->tm_mday) return(1);
560 if (date1->tm_hour<date2->tm_hour) return(-1);
561 if (date1->tm_hour>date2->tm_hour) return(1);
562 if (date1->tm_min<date2->tm_min) return(-1);
563 if (date1->tm_min>date2->tm_min) return(1);
564 if (date1->tm_sec<date2->tm_sec) return(-1);
565 if (date1->tm_sec>date2->tm_sec) return(1);
566 return(0);
567 }
568
buildymd(const char * dia,const char * mes,const char * ano,char * wdata,int wdata_size)569 void buildymd(const char *dia, const char *mes, const char *ano, char *wdata,int wdata_size)
570 {
571 int nmes;
572
573 nmes=month2num(mes);
574 snprintf(wdata,wdata_size,"%04d%02d%02d",atoi(ano),nmes+1,atoi(dia));
575 }
576
577
conv_month(const char * month)578 int conv_month(const char *month)
579 {
580 int x;
581
582 for (x=0; x<12 && strncmp(mtab1[x],month,3)!=0; x++);
583 return(x+1);
584 }
585
586
conv_month_name(int month)587 const char *conv_month_name(int month)
588 {
589 static char str[4];
590
591 if (month<1 || month>12) {
592 snprintf(str,sizeof(str),"%03d",month);
593 return(str);
594 }
595 return(mtab1[month-1]);
596 }
597
598 /*!
599 Write a debug message to stderr. The message is prefixed by "SARG:" to identify its origin.
600
601 \param msg The printf like message to format.
602 \param ... The arguments to format in the message.
603 */
debuga(const char * File,int Line,const char * msg,...)604 void debuga(const char *File,int Line,const char *msg,...)
605 {
606 va_list ap;
607
608 if (debugz>=LogLevel_Source) {
609 /* The path is removed because every source file is in the same directory.
610 * There is no point in reporting the full path from the build directory.
611 */
612 const char *ptr=strrchr(File,'/');
613 if (!ptr) ptr=File;
614 /* TRANSLATORS: This is the prefix to stderr messages when the debug level is
615 set to display the source file (%s) and the line number (%d). */
616 fprintf(stderr,_("SARG(%s:%d): "),ptr,Line);
617 } else {
618 /* TRANSLATORS: This is the prefix to stderr messages when the debug level
619 is low. */
620 fputs(_("SARG: "),stderr);
621 }
622 va_start(ap,msg);
623 vfprintf(stderr,msg,ap);
624 va_end(ap);
625 }
626
627 /*!
628 Write a debug message to stderr. The message is supposed
629 to be displayed after a message from debuga().
630
631 \param msg The printf like message to format.
632 \param ... The arguments to format in the message.
633 */
debuga_more(const char * msg,...)634 void debuga_more(const char *msg,...)
635 {
636 va_list ap;
637
638 va_start(ap,msg);
639 vfprintf(stderr,msg,ap);
640 va_end(ap);
641 }
642
643 /*!
644 Write a debug message to stderr. The message is prefixed by "SARG: (info)".
645
646 \param msg The printf like message to format.
647 \param ... The arguments to format in the message.
648 */
debugaz(const char * File,int Line,const char * msg,...)649 void debugaz(const char *File,int Line,const char *msg,...)
650 {
651 va_list ap;
652
653 if (debugz>=LogLevel_Source) {
654 /* The path is removed because every source file is in the same directory.
655 * There is no point in reporting the full path from the build directory.
656 */
657 const char *ptr=strrchr(File,'/');
658 if (!ptr) ptr=File;
659 /* TRANSLATORS: This is the prefix to information messages when the debug level is
660 set to display the source file (%s) and the line number (%d). */
661 fprintf(stderr,_("SARG(%s:%d): (info) "),ptr,Line);
662 } else {
663 /* TRANSLATORS: This is the prefix to information messages when the debug level
664 is low. */
665 fputs(_("SARG: (info) "),stderr);
666 }
667 va_start(ap,msg);
668 vfprintf(stderr,msg,ap);
669 va_end(ap);
670 }
671
672
fixnum(long long int value,int n)673 char *fixnum(long long int value, int n)
674 {
675 #define MAXLEN_FIXNUM 256
676 char num[MAXLEN_FIXNUM]="";
677 char buf[MAXLEN_FIXNUM * 2];
678 char *pbuf;
679 static char ret[MAXLEN_FIXNUM * 2];
680 char *pret;
681 register int i, j, k;
682 int numlen;
683 static char abbrev[30]="";
684
685 my_lltoa(value, num, sizeof(num), 0);
686
687 if (DisplayedValues==DISPLAY_ABBREV) {
688 numlen = strlen(num);
689 if (numlen <= 3)
690 strcpy(abbrev,num);
691 else if (numlen%3 == 1) {
692 abbrev[0]=num[0];
693 abbrev[1]=(UseComma) ? ',' : '.';
694 abbrev[2]=num[1];
695 abbrev[3]=num[2];
696 abbrev[4]='\0';
697 }
698 else if (numlen%3 == 2) {
699 abbrev[0]=num[0];
700 abbrev[1]=num[1];
701 abbrev[2]=(UseComma) ? ',' : '.';
702 abbrev[3]=num[2];
703 abbrev[4]=num[3];
704 abbrev[5]='\0';
705 }
706 else if (numlen%3 == 0) {
707 abbrev[0]=num[0];
708 abbrev[1]=num[1];
709 abbrev[2]=num[2];
710 abbrev[3]=(UseComma) ? ',' : '.';
711 abbrev[4]=num[3];
712 abbrev[5]=num[4];
713 abbrev[6]='\0';
714 }
715 if (n) {
716 if (numlen <= 3) {
717 //no prefix
718 }
719 else if (numlen <= 6)
720 strcat(abbrev,"K");
721 else if (numlen <= 9)
722 strcat(abbrev,"M");
723 else if (numlen <= 12)
724 strcat(abbrev,"G");
725 else if (numlen <= 15)
726 strcat(abbrev,"T");
727 else if (numlen >= 18)
728 strcat(abbrev,"P");
729 else if (numlen <= 21)
730 strcat(abbrev,"E");
731 else if (numlen <= 24)
732 strcat(abbrev,"Z");
733 else if (numlen <= 27)
734 strcat(abbrev,"Y");
735 else
736 strcat(abbrev,"???");
737 }
738 return(abbrev);
739 }
740
741 memset(buf,0,MAXLEN_FIXNUM*2);
742
743 pbuf = buf;
744 pret = ret;
745 k = 0;
746
747 for ( i = strlen(num) - 1, j = 0 ; i > -1; i--) {
748 if ( k == 2 && i != 0 ) {
749 k = 0;
750 pbuf[j++] = num[i];
751 pbuf[j++] = (UseComma) ? ',' : '.';
752 continue;
753 }
754 pbuf[j] = num[i];
755 j++;
756 k++;
757 }
758
759 pret[0]='\0';
760
761 for ( i = strlen(pbuf) - 1, j = 0 ; i > -1; i--, j++)
762 pret[j] = pbuf[i];
763
764 pret[j] = '\0';
765
766 return pret;
767 }
768
769
fixnum2(long long int value,int n)770 char *fixnum2(long long int value, int n)
771 {
772 #define MAXLEN_FIXNUM2 1024
773 char num[MAXLEN_FIXNUM2];
774 char buf[MAXLEN_FIXNUM2 * 2];
775 char *pbuf;
776 static char ret[MAXLEN_FIXNUM2 * 2];
777 char *pret;
778 register int i, j, k;
779
780 my_lltoa(value, num, sizeof(num), 0);
781 memset(buf,0,MAXLEN_FIXNUM2*2);
782
783 pbuf = buf;
784 pret = ret;
785 k = 0;
786
787 for ( i = strlen(num) - 1, j = 0 ; i > -1; i--) {
788 if ( k == 2 && i != 0 ) {
789 k = 0;
790 pbuf[j++] = num[i];
791 pbuf[j++] = (UseComma) ? ',' : '.';
792 continue;
793 }
794 pbuf[j] = num[i];
795 j++;
796 k++;
797 }
798
799 pret[0]='\0';
800
801 for ( i = strlen(pbuf) - 1, j = 0 ; i > -1; i--, j++)
802 pret[j] = pbuf[i];
803
804 pret[j] = '\0';
805
806 return pret;
807 }
808
809
buildtime(long long int elap)810 char *buildtime(long long int elap)
811 {
812 long int num = elap / 1000LL;
813 int hor = 0;
814 int min = 0;
815 int sec = 0;
816 static char buf[20];
817
818 hor=num / 3600L;
819 min=(num % 3600L) / 60L;
820 sec=num % 60L;
821 snprintf(buf,sizeof(buf),"%02d:%02d:%02d",hor,min,sec);
822
823 return(buf);
824 }
825
826
827 /*!
828 Get the date stored in the <tt>sarg-date</tt> file of a directory with the connection data.
829
830 \param dirname The directory to look for the connection directory.
831 \param name The name of the directory whose <tt>sarg-date</tt> file must be read.
832 \param data The buffer to store the content of the file. It must be more than 80
833 bytes long.
834
835 \retval 0 No error.
836 \retval -1 File not found.
837 */
obtdate(const char * dirname,const char * name,char * data)838 int obtdate(const char *dirname, const char *name, char *data)
839 {
840 FILE *fp_in;
841 char wdir[MAXLEN];
842
843 if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-date",dirname,name)>=sizeof(wdir)) {
844 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
845 debuga_more("%s%s/sarg-date",dirname,name);
846 exit(EXIT_FAILURE);
847 }
848 if ((fp_in = fopen(wdir, "rt")) == 0) {
849 if (snprintf(wdir,sizeof(wdir),"%s%s/date",dirname,name)>=sizeof(wdir)) {
850 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
851 debuga_more("%s%s/date",dirname,name);
852 exit(EXIT_FAILURE);
853 }
854 if ((fp_in = fopen(wdir, "rt")) == 0) {
855 data[0]='\0';
856 return(-1);
857 }
858 }
859
860 if (!fgets(data,80,fp_in)) {
861 debuga(__FILE__,__LINE__,_("Failed to read the date in file \"%s\"\n"),wdir);
862 exit(EXIT_FAILURE);
863 }
864 if (fclose(fp_in)==EOF) {
865 debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wdir,strerror(errno));
866 exit(EXIT_FAILURE);
867 }
868 fixendofline(data);
869
870 return(0);
871 }
872
873
formatdate(char * date,int date_size,int year,int month,int day,int hour,int minute,int second,int dst)874 void formatdate(char *date,int date_size,int year,int month,int day,int hour,int minute,int second,int dst)
875 {
876 struct tm ltm;
877 time_t unixtime;
878 struct tm *fulltm;
879
880 memset(<m,0,sizeof(ltm));
881 if (year>=1900) ltm.tm_year=year-1900;
882 if (month>=1 && month<=12) ltm.tm_mon=month-1;
883 if (day>=1 && day<=31) ltm.tm_mday=day;
884 if (hour>=0 && hour<24) ltm.tm_hour=hour;
885 if (minute>=0 && minute<60) ltm.tm_min=minute;
886 if (second>=0 && second<60) ltm.tm_sec=second;
887 ltm.tm_isdst=dst;
888 unixtime=mktime(<m); //fill the missing entries
889 fulltm=localtime(&unixtime);
890 //strftime(date,date_size,"%a %b %d %H:%M:%S %Z %Y",fulltm);
891 strftime(date,date_size,"%c",fulltm);
892 }
893
894
computedate(int year,int month,int day,struct tm * t)895 void computedate(int year,int month,int day,struct tm *t)
896 {
897 memset(t,0,sizeof(*t));
898 t->tm_year=year-1900;
899 t->tm_mon=month-1;
900 t->tm_mday=day;
901 }
902
903
obtuser(const char * dirname,const char * name)904 int obtuser(const char *dirname, const char *name)
905 {
906 FILE *fp_in;
907 char wdir[MAXLEN];
908 char tuser[20];
909 int nuser;
910
911 if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-users",dirname,name)>=sizeof(wdir)) {
912 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
913 debuga_more("%s%s/sarg-users",dirname,name);
914 exit(EXIT_FAILURE);
915 }
916 if ((fp_in=fopen(wdir,"r"))==NULL) {
917 if (snprintf(wdir,sizeof(wdir),"%s%s/users",dirname,name)>=sizeof(wdir)) {
918 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
919 debuga_more("%s%s/users",dirname,name);
920 exit(EXIT_FAILURE);
921 }
922 if ((fp_in=fopen(wdir,"r"))==NULL) {
923 return(0);
924 }
925 }
926
927 if (!fgets(tuser,sizeof(tuser),fp_in)) {
928 debuga(__FILE__,__LINE__,_("Failed to read the number of users in file \"%s\"\n"),wdir);
929 exit(EXIT_FAILURE);
930 }
931 if (fclose(fp_in)==EOF) {
932 debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wdir,strerror(errno));
933 exit(EXIT_FAILURE);
934 }
935 nuser=atoi(tuser);
936
937 return(nuser);
938 }
939
940
obttotal(const char * dirname,const char * name,int nuser,long long int * tbytes,long long int * media)941 void obttotal(const char *dirname, const char *name, int nuser, long long int *tbytes, long long int *media)
942 {
943 FileObject *fp_in;
944 char *buf;
945 char wdir[MAXLEN];
946 char user[MAX_USER_LEN];
947 char sep;
948 struct getwordstruct gwarea;
949 longline line;
950
951 *tbytes=0;
952 *media=0;
953
954 if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-general",dirname,name)>=sizeof(wdir)) {
955 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
956 debuga_more("%s%s/sarg-general",dirname,name);
957 exit(EXIT_FAILURE);
958 }
959 if ((fp_in = FileObject_Open(wdir)) == NULL) {
960 if (snprintf(wdir,sizeof(wdir),"%s%s/general",dirname,name)>=sizeof(wdir)) {
961 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
962 debuga_more("%s%s/general",dirname,name);
963 exit(EXIT_FAILURE);
964 }
965 if ((fp_in = FileObject_Open(wdir)) == NULL) {
966 return;
967 }
968 }
969
970 if ((line=longline_create())==NULL) {
971 debuga(__FILE__,__LINE__,_("Not enough memory to read file \"%s\"\n"),wdir);
972 exit(EXIT_FAILURE);
973 }
974
975 while((buf=longline_read(fp_in,line))!=NULL) {
976 if (strncmp(buf,"TOTAL\t",6) == 0)
977 sep='\t'; //new file
978 else if (strncmp(buf,"TOTAL ",6) == 0)
979 sep=' '; //old file
980 else
981 continue;
982 getword_start(&gwarea,buf);
983 if (getword(user,sizeof(user),&gwarea,sep)<0) {
984 debuga(__FILE__,__LINE__,_("Invalid user in file \"%s\"\n"),wdir);
985 exit(EXIT_FAILURE);
986 }
987 if (strcmp(user,"TOTAL") != 0)
988 continue;
989 if (getword_skip(MAXLEN,&gwarea,sep)<0) {
990 debuga(__FILE__,__LINE__,_("Invalid total number of accesses in file \"%s\"\n"),wdir);
991 exit(EXIT_FAILURE);
992 }
993 if (getword_atoll(tbytes,&gwarea,sep)<0) {
994 debuga(__FILE__,__LINE__,_("Invalid number of bytes in file \"%s\"\n"),wdir);
995 exit(EXIT_FAILURE);
996 }
997 break;
998 }
999 if (FileObject_Close(fp_in)) {
1000 debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wdir,FileObject_GetLastCloseError());
1001 exit(EXIT_FAILURE);
1002 }
1003 longline_destroy(&line);
1004
1005 if (nuser <= 0)
1006 return;
1007
1008 *media=*tbytes / nuser;
1009 return;
1010 }
1011
getperiod_fromsarglog(const char * arqtt,struct periodstruct * period)1012 int getperiod_fromsarglog(const char *arqtt,struct periodstruct *period)
1013 {
1014 const char *str;
1015 int day0, month0, year0, hour0, minute0;
1016 int day1, month1, year1, hour1, minute1;
1017 int i;
1018
1019 memset(period,0,sizeof(*period));
1020
1021 str=arqtt;
1022 while((str=strstr(str,"sarg-"))!=NULL) {
1023 str+=5;
1024 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
1025 day0=(str[0]-'0')*10+(str[1]-'0');
1026 if (day0<1 || day0>31) continue;
1027 str+=2;
1028 month0=(str[0]-'0')*10+(str[1]-'0')-1;
1029 if (month0<0 || month0>11) continue;
1030 str+=2;
1031 year0=0;
1032 for (i=0 ; isdigit(str[i]) && i<4 ; i++) year0=year0*10+(str[i]-'0');
1033 if (i!=4 || year0<1900) continue;
1034 str+=4;
1035 if (str[0]!='_') continue;
1036 str++;
1037
1038 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
1039 hour0=(str[0]-'0')*10+(str[1]-'0');
1040 str+=2;
1041 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
1042 minute0=(str[0]-'0')*10+(str[1]-'0');
1043 str+=2;
1044
1045 if (*str != '-') continue;
1046 str++;
1047
1048 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
1049 day1=(str[0]-'0')*10+(str[1]-'0');
1050 if (day1<1 || day1>31) continue;
1051 str+=2;
1052 month1=(str[0]-'0')*10+(str[1]-'0')-1;
1053 if (month1<0 || month1>11) continue;
1054 str+=2;
1055 year1=0;
1056 for (i=0 ; isdigit(str[i]) && i<4 ; i++) year1=year1*10+(str[i]-'0');
1057 if (i!=4 || year1<1900) continue;
1058 str+=4;
1059
1060 if (str[0]!='_') continue;
1061 str++;
1062
1063 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
1064 hour1=(str[0]-'0')*10+(str[1]-'0');
1065 str+=2;
1066 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
1067 minute1=(str[0]-'0')*10+(str[1]-'0');
1068 str+=2;
1069
1070 period->start.tm_mday=day0;
1071 period->start.tm_mon=month0;
1072 period->start.tm_year=year0-1900;
1073 period->start.tm_hour=hour0;
1074 period->start.tm_min=minute0;
1075 period->end.tm_mday=day1;
1076 period->end.tm_mon=month1;
1077 period->end.tm_year=year1-1900;
1078 period->end.tm_hour=hour1;
1079 period->end.tm_min=minute1;
1080 return(0);
1081 }
1082 return(-1);
1083 }
1084
1085 /*!
1086 Fill the period with the specified range.
1087
1088 \param period The period to change.
1089 \param ReadFilter Filter containing the date range to write into the period.
1090 */
getperiod_fromrange(struct periodstruct * period,const struct ReadLogDataStruct * ReadFilter)1091 void getperiod_fromrange(struct periodstruct *period,const struct ReadLogDataStruct *ReadFilter)
1092 {
1093 int dfrom=ReadFilter->StartDate;
1094 int duntil=ReadFilter->EndDate;
1095
1096 memset(&period->start,0,sizeof(period->start));
1097 period->start.tm_mday=dfrom%100;
1098 period->start.tm_mon=(dfrom/100)%100-1;
1099 period->start.tm_year=(dfrom/10000)-1900;
1100
1101 memset(&period->end,0,sizeof(period->end));
1102 period->end.tm_mday=duntil%100;
1103 period->end.tm_mon=(duntil/100)%100-1;
1104 period->end.tm_year=(duntil/10000)-1900;
1105 }
1106
1107 /*!
1108 Get the range from a period.
1109
1110 \param period The period to convert to a range.
1111 \param dfrom The variable to store the range beginning. It can be NULL.
1112 \param duntil The variable to store the range end. It can be NULL.
1113 */
getperiod_torange(const struct periodstruct * period,int * dfrom,int * duntil)1114 void getperiod_torange(const struct periodstruct *period,int *dfrom,int *duntil)
1115 {
1116 if (dfrom)
1117 *dfrom=(period->start.tm_year+1900)*10000+(period->start.tm_mon+1)*100+period->start.tm_mday;
1118 if (duntil)
1119 *duntil=(period->end.tm_year+1900)*10000+(period->end.tm_mon+1)*100+period->end.tm_mday;
1120 }
1121
1122 /*!
1123 Update the \a main period to encompass the period in \a candidate.
1124 */
getperiod_merge(struct periodstruct * main,struct periodstruct * candidate)1125 void getperiod_merge(struct periodstruct *main,struct periodstruct *candidate)
1126 {
1127 int cdate;
1128 int mdate;
1129
1130 mdate=(main->start.tm_year)*10000+(main->start.tm_mon)*100+main->start.tm_mday;
1131 cdate=(candidate->start.tm_year)*10000+(candidate->start.tm_mon)*100+candidate->start.tm_mday;
1132 if (mdate==0 || cdate<mdate) memcpy(&main->start,&candidate->start,sizeof(struct tm));
1133
1134 mdate=(main->end.tm_year)*10000+(main->end.tm_mon)*100+main->end.tm_mday;
1135 cdate=(candidate->end.tm_year)*10000+(candidate->end.tm_mon)*100+candidate->end.tm_mday;
1136 if (cdate>mdate) memcpy(&main->end,&candidate->end,sizeof(struct tm));
1137 }
1138
getperiod_buildtext(struct periodstruct * period)1139 int getperiod_buildtext(struct periodstruct *period)
1140 {
1141 int i;
1142 int range;
1143 char text1[40], text2[40];
1144
1145 if (df=='u') {
1146 i=strftime(text1, sizeof(text1), "%Y %b %d", &period->start);
1147 } else if (df=='e') {
1148 i=strftime(text1, sizeof(text1), "%d %b %Y", &period->start);
1149 } else /*if (df=='w')*/ {
1150 IndexTree=INDEX_TREE_FILE;
1151 i=strftime(text1, sizeof(text1), "%Y.%U", &period->start);
1152 }
1153 if (i == 0) return(-1);
1154
1155 range=(period->start.tm_year!=period->end.tm_year ||
1156 period->start.tm_mon!=period->end.tm_mon ||
1157 period->start.tm_mday!=period->end.tm_mday);
1158 if (range) {
1159 if (df=='u') {
1160 i=strftime(text2, sizeof(text2)-i, "%Y %b %d", &period->end);
1161 } else if (df=='e') {
1162 i=strftime(text2, sizeof(text2)-i, "%d %b %Y", &period->end);
1163 } else {
1164 i=strftime(text2, sizeof(text2)-i, "%Y.%U", &period->end);
1165 }
1166 if (i == 0) return(-1);
1167 }
1168
1169 if (range) {
1170 snprintf(period->text,sizeof(period->text),"%s-%s",text1,text2);
1171 snprintf(period->html,sizeof(period->html),"%s—%s",text1,text2);
1172 } else {
1173 safe_strcpy(period->text,text1,sizeof(period->text));
1174 safe_strcpy(period->html,text1,sizeof(period->html));
1175 }
1176 return(0);
1177 }
1178
copy_images(void)1179 static void copy_images(void)
1180 {
1181 FILE *img_in, *img_ou;
1182 char images[512];
1183 char srcfile[MAXLEN];
1184 char dstfile[MAXLEN];
1185 DIR *dirp;
1186 struct dirent *direntp;
1187 char buffer[MAXLEN];
1188 size_t nread;
1189 struct stat info;
1190
1191 if (snprintf(images,sizeof(images),"%simages",outdir)>=sizeof(images)) {
1192 debuga(__FILE__,__LINE__,_("Cannot copy images to target directory %simages\n"),outdir);
1193 exit(EXIT_FAILURE);
1194 }
1195 if (access(images,R_OK)!=0) {
1196 if (PortableMkDir(images,0755)) {
1197 debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),images,strerror(errno));
1198 exit(EXIT_FAILURE);
1199 }
1200 }
1201
1202 dirp = opendir(ImageDir);
1203 if (dirp==NULL) {
1204 debuga(__FILE__,__LINE__,_("Cannot open directory \"%s\": %s\n"),ImageDir,strerror(errno));
1205 return;
1206 }
1207 while ((direntp = readdir( dirp )) != NULL ){
1208 if (direntp->d_name[0]=='.')
1209 continue;
1210 if (snprintf(srcfile,sizeof(srcfile),"%s/%s",ImageDir,direntp->d_name)>=sizeof(srcfile)) {
1211 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
1212 debuga_more("%s/%s",ImageDir,direntp->d_name);
1213 exit(EXIT_FAILURE);
1214 }
1215 if (stat(srcfile,&info)) {
1216 debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),srcfile,strerror(errno));
1217 continue;
1218 }
1219 if (S_ISREG(info.st_mode)) {
1220 if (snprintf(dstfile,sizeof(dstfile),"%s/%s",images,direntp->d_name)>=sizeof(dstfile)) {
1221 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
1222 debuga_more("%s/%s",images,direntp->d_name);
1223 exit(EXIT_FAILURE);
1224 }
1225 img_in = fopen(srcfile, "rb");
1226 if (img_in!=NULL) {
1227 img_ou = fopen(dstfile, "wb");
1228 if (img_ou!=NULL) {
1229 while ((nread = fread(buffer,1,sizeof(buffer),img_in))>0) {
1230 if (fwrite(buffer,1,nread,img_ou)!=nread) {
1231 debuga(__FILE__,__LINE__,_("Failed to copy image \"%s\" to \"%s\"\n"),srcfile,dstfile);
1232 break;
1233 }
1234 }
1235 if (fclose(img_ou)==EOF) {
1236 debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),dstfile,strerror(errno));
1237 exit(EXIT_FAILURE);
1238 }
1239 } else
1240 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"), dstfile, strerror(errno));
1241 if (fclose(img_in)==EOF) {
1242 debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),srcfile,strerror(errno));
1243 exit(EXIT_FAILURE);
1244 }
1245 } else
1246 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"), srcfile, strerror(errno));
1247 }
1248 }
1249 (void) closedir(dirp);
1250
1251 return;
1252 }
1253
1254 /*!
1255 * Check if the proposed file name conforms to the directory structure layed out
1256 * as a file tree. It is used to check if the file name enumerated while scanning
1257 * a directory content may have been created by sarg running with IndexTree set to
1258 * INDEX_TREE_FILE.
1259 */
IsTreeFileDirName(const char * Name)1260 bool IsTreeFileDirName(const char *Name)
1261 {
1262 char DateFormat;
1263 int i;
1264
1265 // start year (date format u) or start day (date format e)
1266 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1267
1268 if (isdigit(Name[2]) && isdigit(Name[3]))
1269 {
1270 // date format is either u or w
1271 if (Name[4]=='.')
1272 {
1273 // date format is w
1274 if (!isdigit(Name[5]) || !isdigit(Name[6])) return(false);
1275 return(true);//date format w is confirmed
1276 }
1277
1278 // date format is u
1279 Name+=4;
1280
1281 // start month
1282 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1283 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1284 if (i<0) return(false);
1285 Name+=3;
1286
1287 // start day
1288 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1289 Name+=2;
1290
1291 DateFormat='u';
1292 }
1293 else if (isalpha(Name[2]) && isalpha(Name[3]) && isalpha(Name[4]))
1294 {
1295 // date format is e
1296 Name+=2;
1297
1298 // start month
1299 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1300 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1301 if (i<0) return(false);
1302 Name+=3;
1303
1304 // start day
1305 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1306 Name+=4;
1307
1308 DateFormat='e';
1309 }
1310 else
1311 return(false);
1312
1313 if (Name[0]!='-') return(false);
1314 Name++;
1315
1316 if (DateFormat=='u')
1317 {
1318 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1319 Name+=4;
1320
1321 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1322 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1323 if (i<0) return(false);
1324 Name+=3;
1325
1326 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1327 Name+=2;
1328 }
1329 else //DateFormat=='e'
1330 {
1331 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1332 Name+=2;
1333
1334 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1335 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1336 if (i<0) return(false);
1337 Name+=3;
1338
1339 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1340 Name+=4;
1341 }
1342 /*
1343 * The directory name may contains additional characters such as a counter if
1344 * a previous report is never overwritten.
1345 */
1346 return(true);
1347 }
1348
1349 /*!
1350 * Check if the proposed file name can be the year part of a report tree build with
1351 * IndexTree set to INDEX_TREE_DATE.
1352 */
IsTreeYearFileName(const char * Name)1353 bool IsTreeYearFileName(const char *Name)
1354 {
1355 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1356 Name+=4;
1357 if (Name[0]=='-')
1358 {
1359 Name++;
1360 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1361 Name+=4;
1362 }
1363 if (Name[0]) return(false);
1364 return(true);
1365 }
1366
1367 /*!
1368 * Check if the proposed file name can be the month part of a report tree build with
1369 * IndexTree set to INDEX_TREE_DATE.
1370 */
IsTreeMonthFileName(const char * Name)1371 bool IsTreeMonthFileName(const char *Name)
1372 {
1373 int m;
1374
1375 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1376 m=(Name[0]-'0')*10+(Name[1]-'0');
1377 if (m<1 || m>12) return(false);
1378 Name+=2;
1379 if (Name[0]=='-')
1380 {
1381 Name++;
1382 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1383 m=(Name[0]-'0')*10+(Name[1]-'0');
1384 if (m<1 || m>12) return(false);
1385 Name+=2;
1386 }
1387 if (Name[0]) return(false);
1388 return(true);
1389 }
1390
1391 /*!
1392 * Check if the proposed file name can be the day part of a report tree build with
1393 * IndexTree set to INDEX_TREE_DATE.
1394 */
IsTreeDayFileName(const char * Name)1395 bool IsTreeDayFileName(const char *Name)
1396 {
1397 int d;
1398
1399 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1400 d=(Name[0]-'0')*10+(Name[1]-'0');
1401 if (d<1 || d>31) return(false);
1402 if (Name[2]=='-')
1403 {
1404 Name+=3;
1405 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1406 d=(Name[0]-'0')*10+(Name[1]-'0');
1407 if (d<1 || d>31) return(false);
1408 }
1409 /*
1410 * The directory name may contains additional characters such as a counter if
1411 * a previous report is never overwritten.
1412 */
1413 return(true);
1414 }
1415
1416 /*!
1417 * Create a directory to generate a report for the specified connection data
1418 * and populate it with the a <tt>sarg-date</tt> file containing the current
1419 * date.
1420 *
1421 * The function also create an <tt>images</tt> directory in \a dir and copy all
1422 * the files from the <tt>SYSCONFDIR/images</tt> into that directory.
1423 *
1424 * \param per1 The date range in the form: YYYYMMMDD-YYYYMMMDD or DDMMMYYYY-DDMMMYYYY depending on the value of
1425 * ::DateFormat.
1426 * \param addr The ip address or host name to which the report is limited. If the string is empty, all the addresses are accepted.
1427 * \param site The destination site to which the report is limited. If the string is empty, all the sites are accepted.
1428 * \param us The user to whom the report is limited. It is an empty string if all the users are accepted.
1429 */
vrfydir(const struct periodstruct * per1,const char * addr,const char * site,const char * us)1430 int vrfydir(const struct periodstruct *per1, const char *addr, const char *site, const char *us)
1431 {
1432 FILE *fp_ou;
1433 char wdir[MAXLEN];
1434 int y1, y2;
1435 int m1, m2;
1436 int d1, d2;
1437 int wlen, wlen2;
1438 time_t curtime;
1439 struct tm *loctm;
1440
1441 strcpy(wdir,outdir);
1442 wlen=strlen(wdir);
1443 y1=per1->start.tm_year+1900;
1444 y2=per1->end.tm_year+1900;
1445 m1=per1->start.tm_mon+1;
1446 m2=per1->end.tm_mon+1;
1447 d1=per1->start.tm_mday;
1448 d2=per1->end.tm_mday;
1449 if (IndexTree == INDEX_TREE_DATE) {
1450 wlen+=sprintf(wdir+wlen,"%04d",y1);
1451 if (y1!=y2) wlen+=sprintf(wdir+wlen,"-%04d",y2);
1452 if (access(wdir, R_OK) != 0)
1453 my_mkdir(wdir);
1454
1455 wlen+=sprintf(wdir+wlen,"/%02d",m1);
1456 if (m1 != m2) wlen+=sprintf(wdir+wlen,"-%02d",m2);
1457 if (access(wdir, R_OK) != 0)
1458 my_mkdir(wdir);
1459
1460 wlen+=sprintf(wdir+wlen,"/%02d",d1);
1461 if (d1!=d2) wlen+=sprintf(wdir+wlen,"-%02d",d2);
1462 } else {
1463 if (df == 'u') {
1464 wlen=snprintf(wdir+wlen,sizeof(wdir)-wlen,"%04d%s%02d-%04d%s%02d",y1,
1465 conv_month_name(m1),d1,y2,conv_month_name(m2),d2);
1466 } else if (df == 'e') {
1467 wlen=snprintf(wdir+wlen,sizeof(wdir)-wlen,"%02d%s%04d-%02d%s%04d",d1,
1468 conv_month_name(m1),y1,d2,conv_month_name(m2),y2);
1469 } else if (df == 'w') {
1470 wlen2=strftime(wdir+wlen, sizeof(wdir)-wlen, "%Y.%U", &per1->start);
1471 if (wlen2==0) return(-1);
1472 wlen+=wlen2;
1473 }
1474 }
1475
1476 if (us[0] != '\0') {
1477 struct userinfostruct *uinfo=userinfo_find_from_id(us);
1478 if (uinfo) {
1479 strcat(wdir,"-");
1480 strcat(wdir,uinfo->filename);
1481 }
1482 }
1483 if (addr[0] != '\0') {
1484 strcat(wdir,"-");
1485 strcat(wdir,addr);
1486 }
1487 if (site[0] != '\0') {
1488 strcat(wdir,"-");
1489 strcat(wdir,site);
1490 }
1491
1492 strcpy(outdirname,wdir);
1493
1494 // manufacture a new unique name if configured to keep old reports or overwrite old report if configured to do so
1495 if (!OverwriteReport) {
1496 int num=1;
1497
1498 while (access(wdir,R_OK)==0 || errno==EACCES) //file exist or can't be read
1499 {
1500 format_path(__FILE__, __LINE__, wdir, sizeof(wdir), "%s.%d", outdirname, num);
1501 num++;
1502 }
1503 if (num>1) {
1504 if (debug)
1505 debuga(__FILE__,__LINE__,_("File \"%s\" already exists, moved to \"%s\"\n"),outdirname,wdir);
1506 rename(outdirname,wdir);
1507 }
1508 } else {
1509 if (access(outdirname,R_OK) == 0) {
1510 unlinkdir(outdirname,1);
1511 }
1512 }
1513 my_mkdir(outdirname);
1514
1515 // create sarg-date to keep track of the report creation date
1516 if (snprintf(wdir,sizeof(wdir),"%s/sarg-date",outdirname)>=sizeof(wdir)) {
1517 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
1518 debuga_more("%s/sarg-date",outdirname);
1519 exit(EXIT_FAILURE);
1520 }
1521 if ((fp_ou = fopen(wdir, "wt")) == 0) {
1522 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),wdir,strerror(errno));
1523 perror("SARG:");
1524 exit(EXIT_FAILURE);
1525 }
1526 time(&curtime);
1527 //strftime(wdir,sizeof(wdir),"%a %b %d %H:%M:%S %Z %Y",localtime(&curtime));
1528 loctm=localtime(&curtime);
1529 strftime(wdir,sizeof(wdir),"%Y-%m-%d %H:%M:%S",loctm);
1530 if (fprintf(fp_ou,"%s %d\n",wdir,loctm->tm_isdst)<0) {
1531 debuga(__FILE__,__LINE__,_("Failed to write the date in \"%s\"\n"),wdir);
1532 perror("SARG:");
1533 exit(EXIT_FAILURE);
1534 }
1535 if (fclose(fp_ou)==EOF) {
1536 debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),wdir,strerror(errno));
1537 exit(EXIT_FAILURE);
1538 }
1539
1540 copy_images();
1541 return(0);
1542 }
1543
1544 /*!
1545 Copy a string without overflowing the buffer. The copied string
1546 is properly terminated by an ASCII zero.
1547
1548 \param dest The destination buffer.
1549 \param src The source buffer.
1550 \param length The size of the destination buffer. The program is aborted
1551 if the length is negative or zero.
1552 */
safe_strcpy(char * dest,const char * src,int length)1553 void safe_strcpy(char *dest,const char *src,int length)
1554 {
1555 if (length<=0) {
1556 debuga(__FILE__,__LINE__,_("Invalid buffer length passed to the function to safely copy a string\n"));
1557 exit(EXIT_FAILURE);
1558 }
1559 strncpy(dest,src,length-1);
1560 dest[length-1]='\0';
1561 }
1562
strip_latin(char * line)1563 void strip_latin(char *line)
1564 {
1565 int i,j;
1566 int skip;
1567
1568 j=0;
1569 skip=0;
1570 for (i=0;line[i];i++){
1571 if (skip){
1572 if (line[i]==';') skip=0;
1573 } else {
1574 if (line[i]=='&')
1575 skip=1;
1576 else
1577 line[j++]=line[i];
1578 }
1579 }
1580 line[j]='\0';
1581 return;
1582 }
1583
zdate(char * ftime,int ftimesize,char DateFormat)1584 void zdate(char *ftime,int ftimesize, char DateFormat)
1585 {
1586 time_t t;
1587 struct tm *local;
1588
1589 t = time(NULL);
1590 local = localtime(&t);
1591 if (DateFormat=='u')
1592 strftime(ftime, ftimesize, "%b/%d/%Y %H:%M", local);
1593 else if (DateFormat=='e')
1594 strftime(ftime, ftimesize, "%d/%b/%Y-%H:%M", local);
1595 else if (DateFormat=='w')
1596 strftime(ftime, ftimesize, "%W-%H-%M", local);
1597 return;
1598 }
1599
1600
fixtime(long long int elap)1601 char *fixtime(long long int elap)
1602 {
1603 long int num = elap / 1000LL;
1604 int hor = 0;
1605 int min = 0;
1606 int sec = 0;
1607 static char buf[20];
1608
1609 hor=num / 3600L;
1610 min=(num % 3600L) / 60L;
1611 sec=num % 60L;
1612
1613 if (hor==0 && min==0 && sec==0)
1614 strcpy(buf,"0");
1615 else
1616 snprintf(buf,sizeof(buf),"%d:%02d:%02d",hor,min,sec);
1617
1618 return buf;
1619 }
1620
1621
date_from(struct ReadLogDataStruct * ReadFilter)1622 void date_from(struct ReadLogDataStruct *ReadFilter)
1623 {
1624 int d0=0;
1625 int m0=0;
1626 int y0=0;
1627 int d1=0;
1628 int m1=0;
1629 int y1=0;
1630
1631 if (isdigit(ReadFilter->DateRange[0])) {
1632 int next=-1;
1633
1634 if (sscanf(ReadFilter->DateRange,"%d/%d/%d%n",&d0,&m0,&y0,&next)!=3 || y0<100 || m0<1 || m0>12 || d0<1 || d0>31 || next<0) {
1635 debuga(__FILE__,__LINE__,_("The date passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1636 exit(EXIT_FAILURE);
1637 }
1638 if (ReadFilter->DateRange[next]=='-') {
1639 if (sscanf(ReadFilter->DateRange+next+1,"%d/%d/%d",&d1,&m1,&y1)!=3 || y1<100 || m1<1 || m1>12 || d1<1 || d1>31) {
1640 debuga(__FILE__,__LINE__,_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1641 exit(EXIT_FAILURE);
1642 }
1643 } else if (ReadFilter->DateRange[next]!='\0') {
1644 debuga(__FILE__,__LINE__,_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1645 exit(EXIT_FAILURE);
1646 } else {
1647 d1=d0;
1648 m1=m0;
1649 y1=y0;
1650 }
1651 } else {
1652 int i;
1653 time_t Today,t1;
1654 struct tm *Date0,Date1;
1655
1656 if (time(&Today)==(time_t)-1) {
1657 debuga(__FILE__,__LINE__,_("Failed to get the current time\n"));
1658 exit(EXIT_FAILURE);
1659 }
1660 if (sscanf(ReadFilter->DateRange,"day-%d",&i)==1) {
1661 if (i<0) {
1662 debuga(__FILE__,__LINE__,_("Invalid number of days in -d parameter\n"));
1663 exit(EXIT_FAILURE);
1664 }
1665 Today-=i*24*60*60;
1666 Date0=localtime(&Today);
1667 if (Date0==NULL) {
1668 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
1669 exit(EXIT_FAILURE);
1670 }
1671 y0=y1=Date0->tm_year+1900;
1672 m0=m1=Date0->tm_mon+1;
1673 d0=d1=Date0->tm_mday;
1674 } else if (sscanf(ReadFilter->DateRange,"week-%d",&i)==1) {
1675 /*
1676 There is no portable way to find the first day of the week even though the
1677 information is available in the locale. nl_langinfo has the unofficial
1678 parameters _NL_TIME_FIRST_WEEKDAY and _NL_TIME_WEEK_1STDAY but they are
1679 undocumented as is their return value and it is discouraged to use them.
1680 Beside, nl_langinfo isn't available on windows and the first day of the
1681 week isn't available at all on that system.
1682 */
1683 const int FirstWeekDay=1;
1684 time_t WeekBegin;
1685
1686 if (i<0) {
1687 debuga(__FILE__,__LINE__,_("Invalid number of weeks in -d parameter\n"));
1688 exit(EXIT_FAILURE);
1689 }
1690 Date0=localtime(&Today);
1691 if (Date0==NULL) {
1692 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
1693 exit(EXIT_FAILURE);
1694 }
1695 WeekBegin=Today-((Date0->tm_wday-FirstWeekDay+7)%7)*24*60*60;
1696 WeekBegin-=i*7*24*60*60;
1697 Date0=localtime(&WeekBegin);
1698 if (Date0==NULL) {
1699 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
1700 exit(EXIT_FAILURE);
1701 }
1702 y0=Date0->tm_year+1900;
1703 m0=Date0->tm_mon+1;
1704 d0=Date0->tm_mday;
1705 WeekBegin+=6*24*60*60;
1706 Date0=localtime(&WeekBegin);
1707 if (Date0==NULL) {
1708 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
1709 exit(EXIT_FAILURE);
1710 }
1711 y1=Date0->tm_year+1900;
1712 m1=Date0->tm_mon+1;
1713 d1=Date0->tm_mday;
1714 } else if (sscanf(ReadFilter->DateRange,"month-%d",&i)==1) {
1715 if (i<0) {
1716 debuga(__FILE__,__LINE__,_("Invalid number of months in -d parameter\n"));
1717 exit(EXIT_FAILURE);
1718 }
1719 Date0=localtime(&Today);
1720 if (Date0==NULL) {
1721 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
1722 exit(EXIT_FAILURE);
1723 }
1724 if (Date0->tm_mon<i%12) {
1725 y0=Date0->tm_year+1900-i/12-1;
1726 m0=(Date0->tm_mon+12-i%12)%12+1;
1727 d0=1;
1728 } else {
1729 y0=Date0->tm_year+1900-i/12;
1730 m0=Date0->tm_mon-i%12+1;
1731 d0=1;
1732 }
1733 memcpy(&Date1,Date0,sizeof(struct tm));
1734 Date1.tm_isdst=-1;
1735 Date1.tm_mday=1;
1736 if (m0<12) {
1737 Date1.tm_mon=m0;
1738 Date1.tm_year=y0-1900;
1739 } else {
1740 Date1.tm_mon=0;
1741 Date1.tm_year=y0-1900+1;
1742 }
1743 t1=mktime(&Date1);
1744 t1-=24*60*60;
1745 Date0=localtime(&t1);
1746 y1=Date0->tm_year+1900;
1747 m1=Date0->tm_mon+1;
1748 d1=Date0->tm_mday;
1749 } else {
1750 debuga(__FILE__,__LINE__,_("Invalid date range passed on command line\n"));
1751 exit(EXIT_FAILURE);
1752 }
1753 }
1754
1755 ReadFilter->StartDate=y0*10000+m0*100+d0;
1756 ReadFilter->EndDate=y1*10000+m1*100+d1;
1757 snprintf(ReadFilter->DateRange,sizeof(ReadFilter->DateRange),"%02d/%02d/%04d-%02d/%02d/%04d",d0,m0,y0,d1,m1,y1);
1758 return;
1759 }
1760
1761
strlow(char * string)1762 char *strlow(char *string)
1763 {
1764 char *s;
1765
1766 if (string)
1767 {
1768 for (s = string; *s; ++s)
1769 *s = tolower(*s);
1770 }
1771
1772 return string;
1773 }
1774
1775
1776
1777
strup(char * string)1778 char *strup(char *string)
1779 {
1780 char *s;
1781
1782 if (string)
1783 {
1784 for (s = string; *s; ++s)
1785 *s = toupper(*s);
1786 }
1787
1788 return string;
1789 }
1790
1791
removetmp(const char * outdir)1792 void removetmp(const char *outdir)
1793 {
1794 FILE *fp_gen;
1795 char filename[256];
1796
1797 if (!RemoveTempFiles)
1798 return;
1799
1800 if (debug) {
1801 debuga(__FILE__,__LINE__,_("Purging temporary file sarg-general\n"));
1802 }
1803 if (snprintf(filename,sizeof(filename),"%s/sarg-general",outdir)>=sizeof(filename)) {
1804 debuga(__FILE__,__LINE__,_("Path too long: "));
1805 debuga_more("%s/sarg-period\n",outdir);
1806 exit(EXIT_FAILURE);
1807 }
1808 if ((fp_gen=fopen(filename,"w"))==NULL){
1809 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),filename,strerror(errno));
1810 exit(EXIT_FAILURE);
1811 }
1812 totalger(fp_gen,filename);
1813 if (fclose(fp_gen)==EOF) {
1814 debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),filename,strerror(errno));
1815 exit(EXIT_FAILURE);
1816 }
1817 }
1818
load_excludecodes(const char * ExcludeCodes)1819 void load_excludecodes(const char *ExcludeCodes)
1820 {
1821 FILE *fp_in;
1822 char data[80];
1823 int i;
1824 int Stored;
1825 long int MemSize;
1826
1827 if (ExcludeCodes[0] == '\0')
1828 return;
1829
1830 if ((fp_in=fopen(ExcludeCodes,"r"))==NULL) {
1831 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
1832 exit(EXIT_FAILURE);
1833 }
1834
1835 if (fseek(fp_in, 0, SEEK_END)==-1) {
1836 debuga(__FILE__,__LINE__,_("Failed to move till the end of file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
1837 exit(EXIT_FAILURE);
1838 }
1839 MemSize = ftell(fp_in);
1840 if (MemSize<0) {
1841 debuga(__FILE__,__LINE__,_("Cannot get the size of file \"%s\"\n"),ExcludeCodes);
1842 exit(EXIT_FAILURE);
1843 }
1844 if (fseek(fp_in, 0, SEEK_SET)==-1) {
1845 debuga(__FILE__,__LINE__,_("Failed to rewind file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
1846 exit(EXIT_FAILURE);
1847 }
1848
1849 MemSize+=1;
1850 if ((excludecode=(char *) malloc(MemSize))==NULL) {
1851 debuga(__FILE__,__LINE__,_("malloc error (%ld bytes required)\n"),MemSize);
1852 exit(EXIT_FAILURE);
1853 }
1854 memset(excludecode,0,MemSize);
1855
1856 Stored=0;
1857 while(fgets(data,sizeof(data),fp_in)!=NULL) {
1858 if (data[0]=='#') continue;
1859 for (i=strlen(data)-1 ; i>=0 && (unsigned char)data[i]<=' ' ; i--) data[i]='\0';
1860 if (i<0) continue;
1861 if (Stored+i+2>=MemSize) {
1862 debuga(__FILE__,__LINE__,_("Too many codes to exclude in file \"%s\"\n"),ExcludeCodes);
1863 break;
1864 }
1865 strcat(excludecode,data);
1866 strcat(excludecode,";");
1867 Stored+=i+1;
1868 }
1869
1870 if (fclose(fp_in)==EOF) {
1871 debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),ExcludeCodes,strerror(errno));
1872 exit(EXIT_FAILURE);
1873 }
1874 return;
1875 }
1876
free_excludecodes(void)1877 void free_excludecodes(void)
1878 {
1879 if (excludecode) {
1880 free(excludecode);
1881 excludecode=NULL;
1882 }
1883 }
1884
vercode(const char * code)1885 int vercode(const char *code)
1886 {
1887 char *cod;
1888 int clen;
1889
1890 if (excludecode && excludecode[0]!='\0') {
1891 clen=strlen(code);
1892 cod=excludecode;
1893 while (cod) {
1894 if (strncmp(code,cod,clen)==0 && cod[clen]==';')
1895 return 1;
1896 cod=strchr(cod,';');
1897 if (cod) cod++;
1898 }
1899 }
1900 return 0;
1901 }
1902
fixnone(char * str)1903 void fixnone(char *str)
1904 {
1905 int i;
1906
1907 for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--);
1908 if (i==3 && strncmp(str,"none",4) == 0)
1909 str[0]='\0';
1910
1911 return;
1912 }
1913
fixendofline(char * str)1914 void fixendofline(char *str)
1915 {
1916 int i;
1917
1918 for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--) str[i]=0;
1919 }
1920
1921 #ifdef LEGACY_TESTVALIDUSERCHAR
testvaliduserchar(const char * user)1922 int testvaliduserchar(const char *user)
1923 {
1924 int x=0;
1925 int y=0;
1926
1927 for (y=0; y<strlen(UserInvalidChar); y++) {
1928 for (x=0; x<strlen(user); x++) {
1929 if (user[x] == UserInvalidChar[y])
1930 return 1;
1931 }
1932 }
1933 return 0;
1934 }
1935 #else
testvaliduserchar(const char * user)1936 int testvaliduserchar(const char *user)
1937 {
1938 char * p_UserInvalidChar = UserInvalidChar ;
1939 const char * p_user ;
1940
1941 while( *p_UserInvalidChar ) {
1942 p_user = user ;
1943 while ( *p_user ) {
1944 if ( *p_UserInvalidChar == *p_user )
1945 return 1;
1946 p_user++ ;
1947 }
1948 p_UserInvalidChar++ ;
1949 }
1950 return 0;
1951 }
1952 #endif
1953
compar(const void * a,const void * b)1954 int compar( const void *a, const void *b )
1955 {
1956 if ( *(int *)a > *(int *)b ) return 1;
1957 if ( *(int *)a < *(int *)b ) return -1;
1958 return 0;
1959 }
1960
1961 /*!
1962 * Store a range in a list.
1963 *
1964 * \param paramname Name of the configuration parameter providing the list.
1965 * \param list List where to store the numbers.
1966 * \param d0 Start range or -1 to store only one value.
1967 * \param d End range if d0>=0 or the single value to store.
1968 */
storenumlist(const char * paramname,int * list,int d0,int d)1969 static void storenumlist(const char *paramname, int *list, int d0, int d)
1970 {
1971 if (d0<0)
1972 {
1973 list[d]=1;
1974 }
1975 else
1976 {
1977 int i;
1978
1979 if (d<d0)
1980 {
1981 debuga(__FILE__,__LINE__,_("Ending value %d is less than or equal to starting value %d in parameter \"%s\"\n"),d,d0,paramname);
1982 exit(EXIT_FAILURE);
1983 }
1984 for (i=d0 ; i<=d ; i++) list[i]=1;
1985 }
1986 }
1987
1988 /*!
1989 Get a comma separated list of numbers and split them into separate values taking into account
1990 that no value may be greater than a maximum. If a value is a range, it is expended.
1991
1992 Any duplicate value is removed.
1993
1994 \param paramname Name of the configuration parameter providing the list.
1995 \param buffer The string with the list of numbers.
1996 \param list List where to store the numbers.
1997 \param maxvalue The maximum value allowed in the list.
1998
1999 The function terminate the application with an error message if the list is invalid.
2000 */
getnumlist(const char * paramname,const char * buffer,int * list,int maxvalue)2001 void getnumlist(const char *paramname, const char *buffer, int *list, int maxvalue)
2002 {
2003 int i, d, d0;
2004 int digitcount;
2005 int nvalues=0;
2006
2007 // skip parameter name
2008 while (*buffer && *buffer!=' ' && *buffer!='\t') buffer++;
2009 if (!*buffer)
2010 {
2011 debuga(__FILE__,__LINE__,_("Missing values for parameter \"%s\"\n"),paramname);
2012 exit(EXIT_FAILURE);
2013 }
2014
2015 // clear list
2016 for (i=0 ; i<maxvalue ; i++) list[i]=0;
2017
2018 // get values
2019 d=0;
2020 d0=-1;
2021 digitcount=0;
2022 for ( ; *buffer ; buffer++)
2023 {
2024 if (isdigit(*buffer))
2025 {
2026 d=d*10+(*buffer-'0');
2027 if (d>=maxvalue)
2028 {
2029 debuga(__FILE__,__LINE__,_("Value too big found in parameter \"%s\" (max value is %d)\n"),paramname,maxvalue-1);
2030 exit(EXIT_FAILURE);
2031 }
2032 digitcount++;
2033 }
2034 else if (*buffer=='-')
2035 {
2036 if (!digitcount)
2037 {
2038 debuga(__FILE__,__LINE__,_("Missing start value before \"-\" in parameter \"%s\"\n"),paramname);
2039 exit(EXIT_FAILURE);
2040 }
2041 d0=d;
2042 d=0;
2043 digitcount=0;
2044 }
2045 else if (*buffer==',')
2046 {
2047 if (!digitcount)
2048 {
2049 debuga(__FILE__,__LINE__,_("Missing value before \",\" in parameter \"%s\"\n"),paramname);
2050 exit(EXIT_FAILURE);
2051 }
2052 storenumlist(paramname,list,d0,d);
2053 nvalues++;
2054 d0=-1;
2055 d=0;
2056 digitcount=0;
2057 }
2058 else if (*buffer=='\r' || *buffer=='\n')
2059 {
2060 break;
2061 }
2062 else if (*buffer!=' ' && *buffer!='\t')
2063 {
2064 debuga(__FILE__,__LINE__,_("Invalid character \"%c\" found in parameter \"%s\"\n"),*buffer,paramname);
2065 exit(EXIT_FAILURE);
2066 }
2067 }
2068 if (digitcount>0)
2069 {
2070 storenumlist(paramname,list,d0,d);
2071 nvalues++;
2072 }
2073 else if (d0>=0)
2074 {
2075 debuga(__FILE__,__LINE__,_("Missing ending value in range for parameter \"%s\"\n"),paramname);
2076 exit(EXIT_FAILURE);
2077 }
2078 if (!nvalues)
2079 {
2080 debuga(__FILE__,__LINE__,_("Parameter \"%s\" is empty\n"),paramname);
2081 exit(EXIT_FAILURE);
2082 }
2083 }
2084
2085 /*!
2086 * Search if the \a list contains the \a value.
2087 *
2088 * \param list The list to search for a value.
2089 * \param maxvalue The maximum value of the list.
2090 * \param value The value to search for.
2091 *
2092 * \return \c True if the value is enabled in the list.
2093 */
numlistcontains(const int * list,int maxvalue,int value)2094 bool numlistcontains(const int *list, int maxvalue, int value)
2095 {
2096 if (value<0 || value>=maxvalue) return(false);
2097 return(list[value]!=0);
2098 }
2099
show_info(FILE * fp_ou)2100 void show_info(FILE *fp_ou)
2101 {
2102 char ftime[127];
2103
2104 if (!ShowSargInfo) return;
2105 zdate(ftime, sizeof(ftime), df);
2106 fputs("<div class=\"info\">",fp_ou);
2107 fprintf(fp_ou,_("Generated by <a href=\"%s\">%s-%s</a> on %s"),URL,PGM,VERSION,ftime);
2108 fputs("</div>\n",fp_ou);
2109 }
2110
show_sarg(FILE * fp_ou,int depth)2111 void show_sarg(FILE *fp_ou, int depth)
2112 {
2113 int i;
2114
2115 if (!ShowSargLogo) return;
2116 fputs("<div class=\"logo\"><a href=\"http://sarg.sourceforge.net\"><img src=\"",fp_ou);
2117 for (i=0 ; i<depth ; i++)
2118 fputs("../",fp_ou);
2119 fputs("images/sarg.png\" title=\"SARG, Squid Analysis Report Generator. Logo by Osamu Matsuzaki\" alt=\"Sarg\"></a> Squid Analysis Report Generator</div>\n",fp_ou);
2120 }
2121
write_logo_image(FILE * fp_ou)2122 void write_logo_image(FILE *fp_ou)
2123 {
2124 if (LogoImage[0]!='\0')
2125 fprintf(fp_ou, "<div class=\"logo\"><img src=\"%s\" width=\"%s\" height=\"%s\" alt=\"Logo\"> %s</div>\n",LogoImage,Width,Height,LogoText);
2126 }
2127
write_html_head(FILE * fp_ou,int depth,const char * page_title,int javascript)2128 void write_html_head(FILE *fp_ou, int depth, const char *page_title,int javascript)
2129 {
2130 int i;
2131
2132 fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n",fp_ou);
2133 fprintf(fp_ou, "<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet);
2134 if (page_title) fprintf(fp_ou,"<title>%s</title>\n",page_title);
2135 css(fp_ou);
2136 if ((javascript & HTML_JS_SORTTABLE)!=0 && SortTableJs[0]) {
2137 fputs("<script type=\"text/javascript\" src=\"",fp_ou);
2138 if (strncmp(SortTableJs,"../",3)==0) {
2139 for (i=0 ; i<depth ; i++) fputs("../",fp_ou);
2140 }
2141 fputs(SortTableJs,fp_ou);
2142 fputs("\"></script>\n",fp_ou);
2143 }
2144 fputs("</head>\n<body>\n",fp_ou);
2145 }
2146
write_html_header(FILE * fp_ou,int depth,const char * page_title,int javascript)2147 void write_html_header(FILE *fp_ou, int depth, const char *page_title,int javascript)
2148 {
2149 write_html_head(fp_ou,depth,page_title,javascript);
2150 write_logo_image(fp_ou);
2151 show_sarg(fp_ou, depth);
2152 fprintf(fp_ou,"<div class=\"title\"><table cellpadding=\"0\" cellspacing=\"0\">\n<tr><th class=\"title_c\">%s</th></tr>\n",Title);
2153 }
2154
close_html_header(FILE * fp_ou)2155 void close_html_header(FILE *fp_ou)
2156 {
2157 fputs("</table></div>\n",fp_ou);
2158 }
2159
write_html_trailer(FILE * fp_ou)2160 void write_html_trailer(FILE *fp_ou)
2161 {
2162 show_info(fp_ou);
2163 fputs("</body>\n</html>\n",fp_ou);
2164 }
2165
output_html_string(FILE * fp_ou,const char * str,int maxlen)2166 void output_html_string(FILE *fp_ou,const char *str,int maxlen)
2167 {
2168 int i=0;
2169
2170 while (*str && (maxlen<=0 || i<maxlen)) {
2171 switch (*str) {
2172 case '&':
2173 fputs("&",fp_ou);
2174 break;
2175 case '<':
2176 fputs("<",fp_ou);
2177 break;
2178 case '>':
2179 fputs(">",fp_ou);
2180 break;
2181 case '"':
2182 fputs(""",fp_ou);
2183 break;
2184 case '\'':
2185 fputs("'",fp_ou);
2186 break;
2187 default:
2188 fputc(*str,fp_ou);
2189 }
2190 str++;
2191 i++;
2192 }
2193 if (maxlen>0 && i>=maxlen)
2194 fputs("…",fp_ou);
2195 }
2196
output_html_url(FILE * fp_ou,const char * url)2197 void output_html_url(FILE *fp_ou,const char *url)
2198 {
2199 while (*url) {
2200 if (*url=='&')
2201 fputs("&",fp_ou);
2202 else
2203 fputc(*url,fp_ou);
2204 url++;
2205 }
2206 }
2207
2208 /*!
2209 Write a host name inside an A tag of a HTML file. If the host name starts
2210 with a star, it is assumed to be an alias that cannot be put inside a link
2211 so the A tag is not written around the host name.
2212
2213 \param fp_ou The handle of the HTML file.
2214 \param url The host to display in the HTML file.
2215 \param maxlen The maximum number of characters to print into the host name.
2216 */
output_html_link(FILE * fp_ou,const char * url,int maxlen)2217 void output_html_link(FILE *fp_ou,const char *url,int maxlen)
2218 {
2219 if (url[0]==ALIAS_PREFIX) {
2220 // this is an alias, no need for a A tag
2221 output_html_string(fp_ou,url+1,100);
2222 } else {
2223 if (skip_scheme(url)==url)
2224 fputs("<a href=\"http://",fp_ou);//no scheme in the url, assume http:// to make the link clickable
2225 else
2226 fputs("<a href=\"",fp_ou);//the scheme is in the url, no need to add one
2227 output_html_url(fp_ou,url);
2228 fputs("\">",fp_ou);
2229 output_html_string(fp_ou,url,100);
2230 fputs("</a>",fp_ou);
2231 }
2232 }
2233
url_module(const char * url,char * w2)2234 void url_module(const char *url, char *w2)
2235 {
2236 int x, y;
2237 char w[255];
2238
2239 y=0;
2240 for (x=strlen(url)-1; x>=0; x--) {
2241 if (url[x] == '/' || y>=sizeof(w)-1) break;
2242 w[y++]=url[x];
2243 }
2244 if (x<0) {
2245 w2[0]='\0';
2246 return;
2247 }
2248
2249 x=0;
2250 for (y=y-1; y>=0; y--) {
2251 w2[x++]=w[y];
2252 }
2253 w2[x]='\0';
2254 }
2255
2256 /*!
2257 Mangle an URL to produce a part that can be used as an anchor in
2258 a html <a name=""> tag.
2259
2260 \param url The URL to mangle.
2261 \param anchor The buffer to write the mangled URL.
2262 \param size The size of the buffer.
2263 */
url_to_anchor(const char * url,char * anchor,int size)2264 void url_to_anchor(const char *url,char *anchor,int size)
2265 {
2266 int i,j;
2267 bool skip;
2268
2269 // find url end
2270 for (i=0 ; url[i] && url[i]!='/' && url[i]!='?' ; i++);
2271 i--;
2272 if (i<=0) {
2273 anchor[0]='\0';
2274 return;
2275 }
2276
2277 // only keep really safe characters
2278 skip=false;
2279 j=size-1;
2280 anchor[j]='\0';
2281 while (j>0 && i>=0)
2282 {
2283 if (isalnum(url[i]) || url[i]=='-' || url[i]=='_' || url[i]=='.') {
2284 anchor[--j]=url[i];
2285 skip=false;
2286 } else {
2287 if (!skip) anchor[--j]='_';
2288 skip=true;
2289 }
2290 i--;
2291 }
2292 if (j>0)
2293 {
2294 while ( anchor[j])
2295 {
2296 *anchor=anchor[j];
2297 anchor++;
2298 }
2299 *anchor='\0';
2300 }
2301 }
2302
version(void)2303 void version(void)
2304 {
2305 printf(_("SARG Version: %s\n"),VERSION);
2306 #if defined(ENABLE_NLS) && defined(HAVE_LOCALE_H)
2307 if (debug) {
2308 printf(_("\nFor the translation to work, a valid message file should be copied to "
2309 "\"%s/<Locale>/LC_MESSAGES/%s.mo\" where <Locale> is derived from the effective locale.\n"),LOCALEDIR,PACKAGE_NAME);
2310 if (CurrentLocale) {
2311 printf(_("Currently effective locale is \"%s\".\n"),CurrentLocale);
2312 } else {
2313 printf(_("Locale is not set in the environment variable.\n"));
2314 }
2315 // TRANSLATORS: You may change this message to tell the reader that the language is correctly supported.
2316 printf(_("If this message is in English, then your language is not supported or not correctly installed.\n"));
2317 }
2318 #endif
2319 if (debug) {
2320 #ifdef HAVE_GLOB_H
2321 printf(_("File globbing compiled in.\n"));
2322 #else
2323 printf(_("File globbing NOT compiled in.\n"));
2324 #endif
2325 }
2326 exit(EXIT_SUCCESS);
2327 }
2328
get_param_value(const char * param,char * line)2329 char *get_param_value(const char *param,char *line)
2330 {
2331 int plen;
2332
2333 while (*line==' ' || *line=='\t') line++;
2334 plen=strlen(param);
2335 if (strncasecmp(line,param,plen)) return(NULL);
2336 if (line[plen]!=' ' && line[plen]!='\t') return(NULL);
2337 line+=plen;
2338 while (*line==' ' || *line=='\t') line++;
2339 return(line);
2340 }
2341
unlinkdir(const char * dir,bool contentonly)2342 void unlinkdir(const char *dir,bool contentonly)
2343 {
2344 struct stat st;
2345 DIR *dirp;
2346 struct dirent *direntp;
2347 char dname[MAXLEN];
2348 int err;
2349
2350 dirp=opendir(dir);
2351 if (!dirp) return;
2352 while ((direntp = readdir(dirp)) != NULL) {
2353 if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
2354 (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
2355 continue;
2356 if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
2357 debuga(__FILE__,__LINE__,_("Path too long: "));
2358 debuga_more("%s/%s\n",dir,direntp->d_name);
2359 exit(EXIT_FAILURE);
2360 }
2361 #ifdef HAVE_LSTAT
2362 err=lstat(dname,&st);
2363 #else
2364 err=stat(dname,&st);
2365 #endif
2366 if (err) {
2367 debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
2368 exit(EXIT_FAILURE);
2369 }
2370 if (S_ISREG(st.st_mode)) {
2371 if (unlink(dname)) {
2372 debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),dname,strerror(errno));
2373 exit(EXIT_FAILURE);
2374 }
2375 } else if (S_ISDIR(st.st_mode)) {
2376 unlinkdir(dname,0);
2377 } else {
2378 debuga(__FILE__,__LINE__,_("Don't know how to delete \"%s\" (not a regular file nor a directory)\n"),dname);
2379 }
2380 }
2381 closedir(dirp);
2382
2383 if (!contentonly) {
2384 if (rmdir(dir)) {
2385 debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),dir,strerror(errno));
2386 exit(EXIT_FAILURE);
2387 }
2388 }
2389 }
2390
2391 /*!
2392 Delete every file from the temporary directory where sarg is told to store its
2393 temporary files.
2394
2395 As any stray file left over by a previous run would be included in the report, we
2396 must delete every file from the temporary directory before we start processing the logs.
2397
2398 But the temporary directory is given by the user either in the configuration file or
2399 on the command line. We check that the user didn't give a wrong directory by looking
2400 at the files stored in the directory. If a single file is not one of ours, we abort.
2401
2402 \param dir The temporary directory to purge.
2403 */
emptytmpdir(const char * dir)2404 void emptytmpdir(const char *dir)
2405 {
2406 struct stat st;
2407 DIR *dirp;
2408 struct dirent *direntp;
2409 int dlen;
2410 int elen;
2411 char dname[MAXLEN];
2412 int err;
2413 int i;
2414 static const char *TmpExt[]=
2415 {
2416 ".int_unsort",
2417 ".int_log",
2418 ".day",
2419 "htmlrel.txt",
2420 ".user_unsort",
2421 ".user_log",
2422 ".utmp",
2423 ".ip",
2424 "lastlog1",
2425 "lastlog",
2426 "emailrep"
2427 };
2428
2429 dirp=opendir(dir);
2430 if (!dirp) return;
2431
2432 // make sure the temporary directory contains only our files
2433 while ((direntp = readdir(dirp)) != NULL) {
2434 if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
2435 (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
2436 continue;
2437
2438 // is it one of our files
2439 dlen=strlen(direntp->d_name);
2440 for (i=sizeof(TmpExt)/sizeof(TmpExt[0])-1 ; i>=0 ; i--) {
2441 elen=strlen(TmpExt[i]);
2442 if (dlen>=elen && strcasecmp(direntp->d_name+dlen-elen,TmpExt[i])==0) break;
2443 }
2444 if (i<0) {
2445 debuga(__FILE__,__LINE__,_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2446 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2447 "directory or manually delete the content of \"%s\"\n"),direntp->d_name,dir,dir);
2448 exit(EXIT_FAILURE);
2449 }
2450
2451 if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
2452 debuga(__FILE__,__LINE__,_("Path too long: "));
2453 debuga_more("%s/%s\n",dir,direntp->d_name);
2454 exit(EXIT_FAILURE);
2455 }
2456
2457 #ifdef HAVE_LSTAT
2458 err=lstat(dname,&st);
2459 #else
2460 err=stat(dname,&st);
2461 #endif
2462 if (err) {
2463 debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
2464 exit(EXIT_FAILURE);
2465 }
2466 if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) {
2467 debuga(__FILE__,__LINE__,_("Unknown path type for \"%s\". Check temporary directory\n"),dname);
2468 exit(EXIT_FAILURE);
2469 }
2470 }
2471 rewinddir(dirp);
2472
2473 // now delete our files
2474 while ((direntp = readdir(dirp)) != NULL) {
2475 if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
2476 (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
2477 continue;
2478
2479 // is it one of our files
2480 dlen=strlen(direntp->d_name);
2481 for (i=sizeof(TmpExt)/sizeof(TmpExt[0])-1 ; i>=0 ; i--) {
2482 elen=strlen(TmpExt[i]);
2483 if (dlen>=elen && strcasecmp(direntp->d_name+dlen-elen,TmpExt[i])==0) break;
2484 }
2485 if (i<0) {
2486 debuga(__FILE__,__LINE__,_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2487 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2488 "directory or manually delete the content of \"%s\"\n"),direntp->d_name,dir,dir);
2489 exit(EXIT_FAILURE);
2490 }
2491
2492 if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
2493 debuga(__FILE__,__LINE__,_("Path too long: "));
2494 debuga_more("%s/%s\n",dir,direntp->d_name);
2495 exit(EXIT_FAILURE);
2496 }
2497 #ifdef HAVE_LSTAT
2498 err=lstat(dname,&st);
2499 #else
2500 err=stat(dname,&st);
2501 #endif
2502 if (err) {
2503 debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
2504 exit(EXIT_FAILURE);
2505 }
2506 if (S_ISDIR(st.st_mode)) {
2507 unlinkdir(dname,0);
2508 } else if (S_ISREG(st.st_mode)) {
2509 if (unlink(dname)) {
2510 debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),dname,strerror(errno));
2511 exit(EXIT_FAILURE);
2512 }
2513 } else {
2514 debuga(__FILE__,__LINE__,_("Don't know how to delete \"%s\" (not a regular file)\n"),dname);
2515 }
2516 }
2517 closedir(dirp);
2518 }
2519
2520 /*!
2521 Extract an url, IPv4 or IPv6 from a buffer. The IP addresses may end with a
2522 prefix size.
2523
2524 \param buf The buffer to parse.
2525 \param text A pointer to set to the beginning of the string pattern. No terminating zero is inserted.
2526 The pointer may be NULL.
2527 \param ipv4 A 4 bytes buffer to store the bytes of the IPv4 address.
2528 \param ipv6 A 8 short integers buffer to store the values of the IPv6 address.
2529 \param nbits The number of prefix bits for an IP address.
2530 \param next The content of the line after the extracted address.
2531
2532 \retval 3 The pattern is a IPv6 address.
2533 \retval 2 The pattern is a IPv4 address.
2534 \retval 1 The patter is a string.
2535 \retval 0 Empty pattern.
2536 */
extract_address_mask(const char * buf,const char ** text,unsigned char * ipv4,unsigned short int * ipv6,int * nbits,const char ** next)2537 int extract_address_mask(const char *buf,const char **text,unsigned char *ipv4,unsigned short int *ipv6,int *nbits,const char **next)
2538 {
2539 int i;
2540 int j;
2541 int ip_size;
2542 unsigned int value4, value6;
2543 unsigned short int addr[8];
2544 int addr_len;
2545 int nibble6_len;
2546 int mask, max_mask;
2547 int pad_pos;
2548 int pad_len;
2549 bool bracket=false;
2550 bool port=false;
2551 int port_num=0;
2552
2553 // skip leading spaces and tabs
2554 while (*buf && (*buf==' ' || *buf=='\t')) buf++;
2555
2556 // find out the nature of the pattern
2557 ip_size=0x60 | 0x04;
2558 if (*buf=='[') {
2559 bracket=true;
2560 ip_size=0x60;
2561 buf++;
2562 }
2563 value4=0U;
2564 value6=0U;
2565 addr_len=0;
2566 nibble6_len=0;
2567 pad_pos=-1;
2568 for (i=0 ; (unsigned char)buf[i]>' ' && buf[i]!='/' && buf[i]!='?' && (!bracket || buf[i]!=']') && ip_size ; i++) {
2569 if (ip_size & 0x04) {
2570 if (isdigit(buf[i])) {
2571 if (port) {
2572 port_num=port_num*10+(buf[i]-'0');
2573 if (port_num>65535) ip_size&=~0x04;
2574 } else {
2575 value4=value4*10+(buf[i]-'0');
2576 if (value4>0xFFU) ip_size&=~0x04;
2577 }
2578 } else if (buf[i]=='.' && addr_len<4) {
2579 addr[addr_len++]=(unsigned short)(value4 & 0xFFU);
2580 value4=0U;
2581 } else if (!port && buf[i]==':') {
2582 port=true;
2583 } else {
2584 ip_size&=~0x04;
2585 }
2586 }
2587 if (ip_size & 0x60) {
2588 if (isdigit(buf[i])) {
2589 value6=(value6<<4)+(buf[i]-'0');
2590 nibble6_len++;
2591 if (value6>0xFFFFU) ip_size&=~0x60;
2592 } else if (toupper(buf[i])>='A' && toupper(buf[i])<='F') {
2593 value6=(value6<<4)+(toupper(buf[i])-'A'+10);
2594 nibble6_len++;
2595 if (value6>0xFFFFU) ip_size&=~0x60;
2596 } else if (buf[i]==':' && addr_len<8) {
2597 if (nibble6_len>0) {
2598 addr[addr_len++]=(unsigned short)(value6 & 0xFFFFU);
2599 nibble6_len=0;
2600 }
2601 value6=0U;
2602 if (buf[i+1]==':') {
2603 pad_pos=addr_len;
2604 i++;
2605 }
2606 } else {
2607 ip_size&=~0x60;
2608 }
2609 }
2610 }
2611 if (i==0) return(0);
2612 if (ip_size & 0x04) {
2613 if (addr_len!=3)
2614 ip_size&=~0x04;
2615 else
2616 addr[addr_len++]=(unsigned short)(value4 & 0xFFU);
2617 }
2618 if (ip_size & 0x60) {
2619 if (pad_pos<0 && addr_len!=7) {
2620 ip_size&=~0x60;
2621 } else if (pad_pos>=0 && addr_len>=7)
2622 ip_size&=~0x60;
2623 else if (nibble6_len>0)
2624 addr[addr_len++]=(unsigned short)(value6 & 0xFFFFU);
2625 }
2626 if (!ip_size) {
2627 if (text) {
2628 *text=buf;
2629 if (bracket) (*text)--;
2630 }
2631 while ((unsigned char)buf[i]>' ') i++;
2632 if (next) *next=buf+i;
2633 return(1);
2634 }
2635 max_mask=(ip_size & 0x04) ? 4*8 : 8*16;
2636 if (buf[i]=='/') {
2637 i++;
2638 mask=atoi(buf+i);
2639 while (isdigit(buf[i])) i++;
2640 if (mask<0 || mask>max_mask) mask=max_mask;
2641 } else
2642 mask=max_mask;
2643 if (ip_size & 0x60 && bracket && buf[i]==']') i++;
2644 if (next) *next=buf+i;
2645 if (ip_size & 0x04) {
2646 if (nbits) *nbits=mask;
2647 for (i=0 ; i<addr_len ; i++)
2648 ipv4[i]=(unsigned char)addr[i];
2649 return(2);
2650 }
2651
2652 // IPv6 address
2653 if (nbits) *nbits=mask;
2654 i=0;
2655 j=0;
2656 if (pad_pos>=0) {
2657 while (i<pad_pos)
2658 ipv6[j++]=(unsigned short int)addr[i++];
2659 pad_len=8-addr_len;
2660 while (j<pad_pos+pad_len)
2661 ipv6[j++]=0;
2662 }
2663 while (i<addr_len)
2664 ipv6[j++]=(unsigned short int)addr[i++];
2665 return(3);
2666 }
2667
format_path(const char * file,int line,char * output_buffer,int buffer_size,const char * format,...)2668 int format_path(const char *file, int line, char *output_buffer, int buffer_size, const char *format,...)
2669 {
2670 va_list ap;
2671 int output_length;
2672
2673 va_start(ap, format);
2674 output_length = vsnprintf(output_buffer, buffer_size, format, ap);
2675 if (output_length >= buffer_size) {
2676 debuga(file, line, _("Path too long: "));
2677 vfprintf(stderr, format, ap);
2678 exit(EXIT_FAILURE);
2679 }
2680 va_end(ap);
2681 return output_length;
2682 }
2683
append_to_path(char * base_path,int base_path_size,const char * append)2684 void append_to_path(char *base_path, int base_path_size, const char *append)
2685 {
2686 int length = strlen(base_path);
2687 int append_length;
2688
2689 if (append[0] == '/') append++;
2690 if (length > 0 && base_path[length-1] != '/') {
2691 if (length+1 >= base_path_size) {
2692 debuga(__FILE__, __LINE__, _("Path too long: "));
2693 fprintf(stderr, "%s/%s", base_path, append);
2694 exit(EXIT_FAILURE);
2695 }
2696 base_path[length++] = '/';
2697 }
2698 append_length = strlen(append);
2699 if (length+append_length >= base_path_size) {
2700 debuga(__FILE__, __LINE__, _("Path too long: "));
2701 base_path[length] = '\0';
2702 fprintf(stderr, "%s%s", base_path, append);
2703 exit(EXIT_FAILURE);
2704 }
2705 strcpy(base_path + length, append);
2706 }
2707