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