]> git.ipfire.org Git - thirdparty/sarg.git/blob - util.c
Delete old reports when index_tree is set to "date"
[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 void name_month(char *month,int month_len)
532 {
533 int x, z=atoi(month)-1;
534 char m[255];
535 char w[20];
536 struct getwordstruct gwarea;
537
538 strcpy(m,_("January,February,March,April,May,June,July,August,September,October,November,December"));
539 getword_start(&gwarea,m);
540
541 for(x=0; x<z; x++)
542 if (getword_multisep(w,sizeof(w),&gwarea,',')<0) {
543 debuga(_("The internal list of month names is invalid. Please report this bug to the translator.\n"));
544 exit(EXIT_FAILURE);
545 }
546 if (getword_multisep(month,month_len,&gwarea,',')<0) {
547 debuga(_("The internal list of month names is invalid. Please report this bug to the translator.\n"));
548 exit(EXIT_FAILURE);
549 }
550 }
551
552
553 /*!
554 Write a debug message to stderr. The message is prefixed by "SARG:" to identify its origin.
555
556 \param msg The printf like message to format.
557 \param ... The arguments to format in the message.
558 */
559 void debuga(const char *msg,...)
560 {
561 va_list ap;
562
563 fputs(_("SARG: "),stderr);
564 va_start(ap,msg);
565 vfprintf(stderr,msg,ap);
566 va_end(ap);
567 }
568
569
570 /*!
571 Write a debug message to stderr. The message is prefixed by "SARG: (info)".
572
573 \param msg The printf like message to format.
574 \param ... The arguments to format in the message.
575 */
576 void debugaz(const char *msg,...)
577 {
578 va_list ap;
579
580 fputs(_("SARG: (info) "),stderr);
581 va_start(ap,msg);
582 vfprintf(stderr,msg,ap);
583 va_end(ap);
584 }
585
586
587 char *fixnum(long long int value, int n)
588 {
589 #define MAXLEN_FIXNUM 256
590 char num[MAXLEN_FIXNUM]="";
591 char buf[MAXLEN_FIXNUM * 2];
592 char *pbuf;
593 static char ret[MAXLEN_FIXNUM * 2];
594 char *pret;
595 register int i, j, k;
596 int numlen;
597 static char abbrev[30]="";
598
599 my_lltoa(value, num, sizeof(num), 0);
600
601 if(DisplayedValues==DISPLAY_ABBREV) {
602 numlen = strlen(num);
603 if(numlen <= 3)
604 strcpy(abbrev,num);
605 else if (numlen%3 == 1) {
606 abbrev[0]=num[0];
607 abbrev[1]=(UseComma) ? ',' : '.';
608 abbrev[2]=num[1];
609 abbrev[3]=num[2];
610 abbrev[4]='\0';
611 }
612 else if (numlen%3 == 2) {
613 abbrev[0]=num[0];
614 abbrev[1]=num[1];
615 abbrev[2]=(UseComma) ? ',' : '.';
616 abbrev[3]=num[2];
617 abbrev[4]=num[3];
618 abbrev[5]='\0';
619 }
620 else if (numlen%3 == 0) {
621 abbrev[0]=num[0];
622 abbrev[1]=num[1];
623 abbrev[2]=num[2];
624 abbrev[3]=(UseComma) ? ',' : '.';
625 abbrev[4]=num[3];
626 abbrev[5]=num[4];
627 abbrev[6]='\0';
628 }
629 if (n) {
630 if (numlen <= 3) {
631 //no prefix
632 }
633 else if (numlen <= 6)
634 strcat(abbrev,"K");
635 else if (numlen <= 9)
636 strcat(abbrev,"M");
637 else if (numlen <= 12)
638 strcat(abbrev,"G");
639 else if (numlen <= 15)
640 strcat(abbrev,"T");
641 else if (numlen >= 18)
642 strcat(abbrev,"P");
643 else if (numlen <= 21)
644 strcat(abbrev,"E");
645 else if (numlen <= 24)
646 strcat(abbrev,"Z");
647 else if (numlen <= 27)
648 strcat(abbrev,"Y");
649 else
650 strcat(abbrev,"???");
651 }
652 return(abbrev);
653 }
654
655 bzero(buf, MAXLEN_FIXNUM*2);
656
657 pbuf = buf;
658 pret = ret;
659 k = 0;
660
661 for ( i = strlen(num) - 1, j = 0 ; i > -1; i--) {
662 if ( k == 2 && i != 0 ) {
663 k = 0;
664 pbuf[j++] = num[i];
665 pbuf[j++] = (UseComma) ? ',' : '.';
666 continue;
667 }
668 pbuf[j] = num[i];
669 j++;
670 k++;
671 }
672
673 pret[0]='\0';
674
675 for ( i = strlen(pbuf) - 1, j = 0 ; i > -1; i--, j++)
676 pret[j] = pbuf[i];
677
678 pret[j] = '\0';
679
680 return pret;
681 }
682
683
684 char *fixnum2(long long int value, int n)
685 {
686 #define MAXLEN_FIXNUM2 1024
687 char num[MAXLEN_FIXNUM2];
688 char buf[MAXLEN_FIXNUM2 * 2];
689 char *pbuf;
690 static char ret[MAXLEN_FIXNUM2 * 2];
691 char *pret;
692 register int i, j, k;
693
694 my_lltoa(value, num, sizeof(num), 0);
695 bzero(buf, MAXLEN_FIXNUM2*2);
696
697 pbuf = buf;
698 pret = ret;
699 k = 0;
700
701 for ( i = strlen(num) - 1, j = 0 ; i > -1; i--) {
702 if ( k == 2 && i != 0 ) {
703 k = 0;
704 pbuf[j++] = num[i];
705 pbuf[j++] = (UseComma) ? ',' : '.';
706 continue;
707 }
708 pbuf[j] = num[i];
709 j++;
710 k++;
711 }
712
713 pret[0]='\0';
714
715 for ( i = strlen(pbuf) - 1, j = 0 ; i > -1; i--, j++)
716 pret[j] = pbuf[i];
717
718 pret[j] = '\0';
719
720 return pret;
721 }
722
723
724 char *buildtime(long long int elap)
725 {
726 long int num = elap / 1000LL;
727 int hor = 0;
728 int min = 0;
729 int sec = 0;
730 static char buf[20];
731
732 hor=num / 3600L;
733 min=(num % 3600L) / 60L;
734 sec=num % 60L;
735 snprintf(buf,sizeof(buf),"%02d:%02d:%02d",hor,min,sec);
736
737 return(buf);
738 }
739
740
741 /*!
742 Get the date stored in the <tt>sarg-date</tt> file of a directory with the connection data.
743
744 \param dirname The directory to look for the connection directory.
745 \param name The name of the directory whose <tt>sarg-date</tt> file must be read.
746 \param data The buffer to store the content of the file. It must be more than 80
747 bytes long.
748
749 \retval 0 No error.
750 \retval -1 File not found.
751 */
752 int obtdate(const char *dirname, const char *name, char *data)
753 {
754 FILE *fp_in;
755 char wdir[MAXLEN];
756
757 if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-date",dirname,name)>=sizeof(wdir)) {
758 debuga(_("Buffer to small to store %s%s/sarg-date"),dirname,name);
759 exit(EXIT_FAILURE);
760 }
761 if ((fp_in = fopen(wdir, "rt")) == 0) {
762 if (snprintf(wdir,sizeof(wdir),"%s%s/date",dirname,name)>=sizeof(wdir)) {
763 debuga(_("Buffer to small to store %s%s/date"),dirname,name);
764 exit(EXIT_FAILURE);
765 }
766 if ((fp_in = fopen(wdir, "rt")) == 0) {
767 data[0]='\0';
768 return(-1);
769 }
770 }
771
772 if (!fgets(data,80,fp_in)) {
773 debuga(_("Failed to read the date in %s\n"),wdir);
774 exit(EXIT_FAILURE);
775 }
776 fclose(fp_in);
777 fixendofline(data);
778
779 return(0);
780 }
781
782
783 void formatdate(char *date,int date_size,int year,int month,int day,int hour,int minute,int second,int dst)
784 {
785 struct tm ltm;
786 time_t unixtime;
787 struct tm *fulltm;
788
789 memset(&ltm,0,sizeof(ltm));
790 if (year>=1900) ltm.tm_year=year-1900;
791 if (month>=1 && month<=12) ltm.tm_mon=month-1;
792 if (day>=1 && day<=31) ltm.tm_mday=day;
793 if (hour>=0 && hour<24) ltm.tm_hour=hour;
794 if (minute>=0 && minute<60) ltm.tm_min=minute;
795 if (second>=0 && second<60) ltm.tm_sec=second;
796 ltm.tm_isdst=dst;
797 unixtime=mktime(&ltm); //fill the missing entries
798 fulltm=localtime(&unixtime);
799 //strftime(date,date_size,"%a %b %d %H:%M:%S %Z %Y",fulltm);
800 strftime(date,date_size,"%c",fulltm);
801 }
802
803
804 void computedate(int year,int month,int day,struct tm *t)
805 {
806 memset(t,0,sizeof(*t));
807 t->tm_year=year-1900;
808 t->tm_mon=month-1;
809 t->tm_mday=day;
810 }
811
812
813 int obtuser(const char *dirname, const char *name)
814 {
815 FILE *fp_in;
816 char wdir[MAXLEN];
817 char tuser[20];
818 int nuser;
819
820 if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-users",dirname,name)>=sizeof(wdir)) {
821 debuga(_("Buffer too small to store %s%s/sarg-users"),dirname,name);
822 exit(EXIT_FAILURE);
823 }
824 if((fp_in=fopen(wdir,"r"))==NULL) {
825 if (snprintf(wdir,sizeof(wdir),"%s%s/users",dirname,name)>=sizeof(wdir)) {
826 debuga(_("Buffer too small to store %s%s/users"),dirname,name);
827 exit(EXIT_FAILURE);
828 }
829 if((fp_in=fopen(wdir,"r"))==NULL) {
830 return(0);
831 }
832 }
833
834 if (!fgets(tuser,sizeof(tuser),fp_in)) {
835 debuga(_("Failed to read the number of users in %s\n"),wdir);
836 exit(EXIT_FAILURE);
837 }
838 fclose(fp_in);
839 nuser=atoi(tuser);
840
841 return(nuser);
842 }
843
844
845 void obttotal(const char *dirname, const char *name, int nuser, long long int *tbytes, long long int *media)
846 {
847 FILE *fp_in;
848 char *buf;
849 char wdir[MAXLEN];
850 char user[MAX_USER_LEN];
851 char sep;
852 struct getwordstruct gwarea;
853 longline line;
854
855 *tbytes=0;
856 *media=0;
857
858 if (snprintf(wdir,sizeof(wdir),"%s%s/sarg-general",dirname,name)>=sizeof(wdir)) {
859 debuga(_("Buffer too small to store %s%s/sarg-general"),dirname,name);
860 exit(EXIT_FAILURE);
861 }
862 if ((fp_in = fopen(wdir, "r")) == 0) {
863 if (snprintf(wdir,sizeof(wdir),"%s%s/general",dirname,name)>=sizeof(wdir)) {
864 debuga(_("Buffer too small to store %s%s/general"),dirname,name);
865 exit(EXIT_FAILURE);
866 }
867 if ((fp_in = fopen(wdir, "r")) == 0) {
868 return;
869 }
870 }
871
872 if ((line=longline_create())==NULL) {
873 debuga(_("Not enough memory to read the file %s\n"),wdir);
874 exit(EXIT_FAILURE);
875 }
876
877 while((buf=longline_read(fp_in,line))!=NULL) {
878 if (strncmp(buf,"TOTAL\t",6) == 0)
879 sep='\t'; //new file
880 else if (strncmp(buf,"TOTAL ",6) == 0)
881 sep=' '; //old file
882 else
883 continue;
884 getword_start(&gwarea,buf);
885 if (getword(user,sizeof(user),&gwarea,sep)<0) {
886 debuga(_("There is a invalid user in file %s\n"),wdir);
887 exit(EXIT_FAILURE);
888 }
889 if(strcmp(user,"TOTAL") != 0)
890 continue;
891 if (getword_skip(MAXLEN,&gwarea,sep)<0) {
892 debuga(_("There a broken total number of access in file %s\n"),wdir);
893 exit(EXIT_FAILURE);
894 }
895 if (getword_atoll(tbytes,&gwarea,sep)<0) {
896 debuga(_("There is a broken number of bytes in file %s\n"),wdir);
897 exit(EXIT_FAILURE);
898 }
899 break;
900 }
901 fclose(fp_in);
902 longline_destroy(&line);
903
904 if (nuser <= 0)
905 return;
906
907 *media=*tbytes / nuser;
908 return;
909 }
910
911 int getperiod_fromsarglog(const char *arqtt,struct periodstruct *period)
912 {
913 const char *str;
914 int day0, month0, year0, hour0, minute0;
915 int day1, month1, year1, hour1, minute1;
916 int i;
917
918 memset(period,0,sizeof(*period));
919
920 str=arqtt;
921 while((str=strstr(str,"sarg-"))!=NULL) {
922 str+=5;
923 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
924 day0=(str[0]-'0')*10+(str[1]-'0');
925 if (day0<1 || day0>31) continue;
926 str+=2;
927 month0=(str[0]-'0')*10+(str[1]-'0')-1;
928 if (month0<0 || month0>11) continue;
929 str+=2;
930 year0=0;
931 for (i=0 ; isdigit(str[i]) && i<4 ; i++) year0=year0*10+(str[i]-'0');
932 if (i!=4) continue;
933 str+=4;
934 if (str[0]!='_') continue;
935 str++;
936
937 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
938 hour0=(str[0]-'0')*10+(str[1]-'0');
939 str+=2;
940 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
941 minute0=(str[0]-'0')*10+(str[1]-'0');
942 str+=2;
943
944 if (*str != '-') continue;
945 str++;
946
947 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
948 day1=(str[0]-'0')*10+(str[1]-'0');
949 if (day1<1 || day1>31) continue;
950 str+=2;
951 month1=(str[0]-'0')*10+(str[1]-'0')-1;
952 if (month1<0 || month1>11) continue;
953 str+=2;
954 year1=0;
955 for (i=0 ; isdigit(str[i]) && i<4 ; i++) year1=year1*10+(str[i]-'0');
956 if (i!=4) continue;
957 str+=4;
958
959 if (str[0]!='_') continue;
960 str++;
961
962 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
963 hour1=(str[0]-'0')*10+(str[1]-'0');
964 str+=2;
965 if (!isdigit(str[0]) || !isdigit(str[1])) continue;
966 minute1=(str[0]-'0')*10+(str[1]-'0');
967 str+=2;
968
969 period->start.tm_mday=day0;
970 period->start.tm_mon=month0;
971 period->start.tm_year=year0-1900;
972 period->start.tm_hour=hour0;
973 period->start.tm_min=minute0;
974 period->end.tm_mday=day1;
975 period->end.tm_mon=month1;
976 period->end.tm_year=year1-1900;
977 period->end.tm_hour=hour1;
978 period->end.tm_min=minute1;
979 return(0);
980 }
981 return(-1);
982 }
983
984 void getperiod_fromrange(struct periodstruct *period,int dfrom,int duntil)
985 {
986 memset(&period->start,0,sizeof(period->start));
987 period->start.tm_mday=dfrom%100;
988 period->start.tm_mon=(dfrom/100)%100-1;
989 period->start.tm_year=(dfrom/10000)-1900;
990
991 memset(&period->end,0,sizeof(period->end));
992 period->end.tm_mday=duntil%100;
993 period->end.tm_mon=(duntil/100)%100-1;
994 period->end.tm_year=(duntil/10000)-1900;
995 }
996
997 /*!
998 Update the \a main period to encompass the period in \a candidate.
999 */
1000 void getperiod_merge(struct periodstruct *main,struct periodstruct *candidate)
1001 {
1002 int cdate;
1003 int mdate;
1004
1005 mdate=(main->start.tm_year)*10000+(main->start.tm_mon)*100+main->start.tm_mday;
1006 cdate=(candidate->start.tm_year)*10000+(candidate->start.tm_mon)*100+candidate->start.tm_mday;
1007 if (cdate<mdate) memcpy(&main->start,&candidate->start,sizeof(struct tm));
1008
1009 mdate=(main->end.tm_year)*10000+(main->end.tm_mon)*100+main->end.tm_mday;
1010 cdate=(candidate->end.tm_year)*10000+(candidate->end.tm_mon)*100+candidate->end.tm_mday;
1011 if (cdate>mdate) memcpy(&main->end,&candidate->end,sizeof(struct tm));
1012 }
1013
1014 int getperiod_buildtext(struct periodstruct *period)
1015 {
1016 int i;
1017 int range;
1018 char text1[40], text2[40];
1019
1020 if (df=='u') {
1021 i=strftime(text1, sizeof(text1), "%Y %b %d", &period->start);
1022 } else if(df=='e') {
1023 i=strftime(text1, sizeof(text1), "%d %b %Y", &period->start);
1024 } else /*if (df=='w')*/ {
1025 IndexTree=INDEX_TREE_FILE;
1026 i=strftime(text1, sizeof(text1), "%Y.%U", &period->start);
1027 }
1028 if (i == 0) return(-1);
1029
1030 range=(period->start.tm_year!=period->end.tm_year ||
1031 period->start.tm_mon!=period->end.tm_mon ||
1032 period->start.tm_mday!=period->end.tm_mday);
1033 if (range) {
1034 if (df=='u') {
1035 i=strftime(text2, sizeof(text2)-i, "%Y %b %d", &period->end);
1036 } else if (df=='e') {
1037 i=strftime(text2, sizeof(text2)-i, "%d %b %Y", &period->end);
1038 } else {
1039 i=strftime(text2, sizeof(text2)-i, "%Y.%U", &period->end);
1040 }
1041 if (i == 0) return(-1);
1042 }
1043
1044 if (range) {
1045 snprintf(period->text,sizeof(period->text),"%s-%s",text1,text2);
1046 snprintf(period->html,sizeof(period->html),"%s&mdash;%s",text1,text2);
1047 } else {
1048 safe_strcpy(period->text,text1,sizeof(period->text));
1049 safe_strcpy(period->html,text1,sizeof(period->html));
1050 }
1051 return(0);
1052 }
1053
1054 static void copy_images(void)
1055 {
1056 FILE *img_in, *img_ou;
1057 char images[512];
1058 char imgdir[MAXLEN];
1059 char srcfile[MAXLEN];
1060 char dstfile[MAXLEN];
1061 DIR *dirp;
1062 struct dirent *direntp;
1063 char buffer[MAXLEN];
1064 size_t nread;
1065 struct stat info;
1066
1067 if (snprintf(images,sizeof(images),"%simages",outdir)>=sizeof(images)) {
1068 debuga(_("Cannot copy images to target directory %simages\n"),outdir);
1069 exit(EXIT_FAILURE);
1070 }
1071 if (access(images,R_OK)!=0) {
1072 if (mkdir(images,0755)) {
1073 debuga(_("Cannot create directory %s - %s\n"),images,strerror(errno));
1074 exit(EXIT_FAILURE);
1075 }
1076 }
1077
1078 strcpy(imgdir,IMAGEDIR);
1079 dirp = opendir(imgdir);
1080 if(dirp==NULL) {
1081 debuga(_("(util) Can't open directory %s: %s\n"),imgdir,strerror(errno));
1082 return;
1083 }
1084 while ((direntp = readdir( dirp )) != NULL ){
1085 if(direntp->d_name[0]=='.')
1086 continue;
1087 if (snprintf(srcfile,sizeof(srcfile),"%s/%s",imgdir,direntp->d_name)>=sizeof(srcfile)) {
1088 debuga(_("Buffer too small to store %s/%s"),imgdir,direntp->d_name);
1089 exit(EXIT_FAILURE);
1090 }
1091 if (stat(srcfile,&info)) {
1092 debuga(_("Cannot stat \"%s\" - %s\n"),srcfile,strerror(errno));
1093 continue;
1094 }
1095 if (S_ISREG(info.st_mode)) {
1096 if (snprintf(dstfile,sizeof(dstfile),"%s/%s",images,direntp->d_name)>=sizeof(dstfile)) {
1097 debuga(_("Buffer too small to store %s/%s"),images,direntp->d_name);
1098 exit(EXIT_FAILURE);
1099 }
1100 img_in = fopen(srcfile, "rb");
1101 if(img_in!=NULL) {
1102 img_ou = fopen(dstfile, "wb");
1103 if(img_ou!=NULL) {
1104 while ((nread = fread(buffer,1,sizeof(buffer),img_in))>0) {
1105 if (fwrite(buffer,1,nread,img_ou)!=nread) {
1106 debuga(_("Failed to copy image %s to %s\n"),srcfile,dstfile);
1107 break;
1108 }
1109 }
1110 if (fclose(img_ou)==EOF) {
1111 debuga(_("Error while copying image %s: %s\n"),dstfile,strerror(errno));
1112 exit(EXIT_FAILURE);
1113 }
1114 } else
1115 fprintf(stderr,"SARG: (util): %s %s: %s\n", _("Cannot open file")?_("Cannot open file"):"Can't open/create file", dstfile, strerror(errno));
1116 fclose(img_in);
1117 } else
1118 fprintf(stderr,"SARG: (util): %s %s: %s\n", _("Cannot open file")?_("Cannot open file"):"Can't open file", srcfile, strerror(errno));
1119 }
1120 }
1121 (void) closedir(dirp);
1122
1123 return;
1124 }
1125
1126 /*!
1127 * Check if the proposed file name conforms to the directory structure layed out
1128 * as a file tree. It is used to check if the file name enumerated while scanning
1129 * a directory content may have been created by sarg running with IndexTree set to
1130 * INDEX_TREE_FILE.
1131 */
1132 bool IsTreeFileDirName(const char *Name)
1133 {
1134 char DateFormat;
1135 int i;
1136
1137 // start year (date format u) or start day (date format e)
1138 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1139
1140 if (isdigit(Name[2]) && isdigit(Name[3]))
1141 {
1142 // date format is either u or w
1143 if (Name[4]=='.')
1144 {
1145 // date format is w
1146 if (!isdigit(Name[5]) || !isdigit(Name[6])) return(false);
1147 return(true);//date format w is confirmed
1148 }
1149
1150 // date format is u
1151 Name+=4;
1152
1153 // start month
1154 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1155 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1156 if (i<0) return(false);
1157 Name+=3;
1158
1159 // start day
1160 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1161 Name+=2;
1162
1163 DateFormat='u';
1164 }
1165 else if (isalpha(Name[2]) && isalpha(Name[3]) && isalpha(Name[4]))
1166 {
1167 // date format is e
1168 Name+=2;
1169
1170 // start month
1171 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1172 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1173 if (i<0) return(false);
1174 Name+=3;
1175
1176 // start day
1177 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1178 Name+=4;
1179
1180 DateFormat='e';
1181 }
1182 else
1183 return(false);
1184
1185 if (Name[0]!='-') return(false);
1186 Name++;
1187
1188 if (DateFormat=='u')
1189 {
1190 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1191 Name+=4;
1192
1193 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1194 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1195 if (i<0) return(false);
1196 Name+=3;
1197
1198 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1199 Name+=2;
1200 }
1201 else //DateFormat=='e'
1202 {
1203 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1204 Name+=2;
1205
1206 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1207 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1208 if (i<0) return(false);
1209 Name+=3;
1210
1211 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1212 Name+=4;
1213 }
1214 /*
1215 * The directory name may contains additional characters such as a counter if
1216 * a previous report is never overwritten.
1217 */
1218 return(true);
1219 }
1220
1221 /*!
1222 * Check if the proposed file name can be the year part of a report tree build with
1223 * IndexTree set to INDEX_TREE_DATE.
1224 */
1225 bool IsTreeYearFileName(const char *Name)
1226 {
1227 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1228 Name+=4;
1229 if (Name[0]=='-')
1230 {
1231 Name++;
1232 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1233 Name+=4;
1234 }
1235 if (Name[0]) return(false);
1236 return(true);
1237 }
1238
1239 /*!
1240 * Check if the proposed file name can be the month part of a report tree build with
1241 * IndexTree set to INDEX_TREE_DATE.
1242 */
1243 bool IsTreeMonthFileName(const char *Name)
1244 {
1245 int m;
1246
1247 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1248 m=(Name[0]-'0')*10+(Name[1]-'0');
1249 if (m<1 || m>12) return(false);
1250 Name+=2;
1251 if (Name[0]=='-')
1252 {
1253 Name++;
1254 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1255 m=(Name[0]-'0')*10+(Name[1]-'0');
1256 if (m<1 || m>12) return(false);
1257 Name+=2;
1258 }
1259 if (Name[0]) return(false);
1260 return(true);
1261 }
1262
1263 /*!
1264 * Check if the proposed file name can be the day part of a report tree build with
1265 * IndexTree set to INDEX_TREE_DATE.
1266 */
1267 bool IsTreeDayFileName(const char *Name)
1268 {
1269 int d;
1270
1271 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1272 d=(Name[0]-'0')*10+(Name[1]-'0');
1273 if (d<1 || d>31) return(false);
1274 if (Name[2]=='-')
1275 {
1276 Name+=3;
1277 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1278 d=(Name[0]-'0')*10+(Name[1]-'0');
1279 if (d<1 || d>31) return(false);
1280 }
1281 /*
1282 * The directory name may contains additional characters such as a counter if
1283 * a previous report is never overwritten.
1284 */
1285 return(true);
1286 }
1287
1288 int vrfydir(const struct periodstruct *per1, const char *addr, const char *site, const char *us, const char *form)
1289 {
1290 FILE *fp_ou;
1291 int num=1, count=0;
1292 char wdir[MAXLEN];
1293 char dirname2[MAXLEN];
1294 int y1, y2;
1295 int m1, m2;
1296 int d1, d2;
1297 int wlen, wlen2;
1298 time_t curtime;
1299 struct tm *loctm;
1300
1301 strcpy(wdir,outdir);
1302 wlen=strlen(wdir);
1303 y1=per1->start.tm_year+1900;
1304 y2=per1->end.tm_year+1900;
1305 m1=per1->start.tm_mon+1;
1306 m2=per1->end.tm_mon+1;
1307 d1=per1->start.tm_mday;
1308 d2=per1->end.tm_mday;
1309 if(IndexTree == INDEX_TREE_DATE) {
1310 wlen+=sprintf(wdir+wlen,"%04d",y1);
1311 if(y1!=y2) wlen+=sprintf(wdir+wlen,"-%04d",y2);
1312 if(access(wdir, R_OK) != 0)
1313 my_mkdir(wdir);
1314
1315 wlen+=sprintf(wdir+wlen,"/%02d",m1);
1316 if(m1 != m2) wlen+=sprintf(wdir+wlen,"-%02d",m2);
1317 if(access(wdir, R_OK) != 0)
1318 my_mkdir(wdir);
1319
1320 wlen+=sprintf(wdir+wlen,"/%02d",d1);
1321 if(d1!=d2) wlen+=sprintf(wdir+wlen,"-%02d",d2);
1322 } else {
1323 if (df == 'u') {
1324 wlen=snprintf(wdir+wlen,sizeof(wdir)-wlen,"%04d%s%02d-%04d%s%02d",y1,
1325 conv_month_name(m1),d1,y2,conv_month_name(m2),d2);
1326 } else if (df == 'e') {
1327 wlen=snprintf(wdir+wlen,sizeof(wdir)-wlen,"%02d%s%04d-%02d%s%04d",d1,
1328 conv_month_name(m1),y1,d2,conv_month_name(m2),y2);
1329 } else if (df == 'w') {
1330 wlen2=strftime(wdir+wlen, sizeof(wdir)-wlen, "%Y.%U", &per1->start);
1331 if (wlen2==0) return(-1);
1332 wlen+=wlen2;
1333 }
1334 }
1335
1336 if(us[0] != '\0') {
1337 struct userinfostruct *uinfo=userinfo_find_from_id(us);
1338 if (uinfo) {
1339 strcat(wdir,"-");
1340 strcat(wdir,uinfo->filename);
1341 }
1342 }
1343 if(addr[0] != '\0') {
1344 strcat(wdir,"-");
1345 strcat(wdir,addr);
1346 }
1347 if(site[0] != '\0') {
1348 strcat(wdir,"-");
1349 strcat(wdir,site);
1350 }
1351
1352 strcpy(outdirname,wdir);
1353
1354 if(IndexTree != INDEX_TREE_DATE) {
1355 if(!OverwriteReport) {
1356 while(num) {
1357 if(access(wdir,R_OK) == 0) {
1358 sprintf(wdir,"%s.%d",outdirname,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"),outdirname,wdir);
1368 rename(outdirname,wdir);
1369 }
1370 } else {
1371 if(access(outdirname,R_OK) == 0) {
1372 unlinkdir(outdirname,1);
1373 }
1374 }
1375 my_mkdir(outdirname);
1376 } else {
1377 strcpy(dirname2,wdir);
1378 if(!OverwriteReport) {
1379 while(num) {
1380 if(access(wdir,R_OK) == 0) {
1381 sprintf(wdir,"%s.%d",dirname2,num);
1382 num++;
1383 count++;
1384 } else
1385 break;
1386 }
1387
1388 if(count > 0) {
1389 if(debug)
1390 debuga(_("File %s already exists, moved to %s\n"),dirname2,wdir);
1391 rename(dirname2,wdir);
1392 strcpy(dirname2,wdir);
1393 }
1394 } else {
1395 if(access(wdir,R_OK) == 0) {
1396 unlinkdir(wdir,1);
1397 }
1398 }
1399
1400 if(access(wdir, R_OK) != 0)
1401 my_mkdir(wdir);
1402 }
1403
1404 strcpy(dirname2,wdir);
1405
1406 if (snprintf(wdir,sizeof(wdir),"%s/sarg-date",outdirname)>=sizeof(wdir)) {
1407 debuga(_("Buffer too small to store %s/sarg-date"),outdirname);
1408 exit(EXIT_FAILURE);
1409 }
1410 if ((fp_ou = fopen(wdir, "wt")) == 0) {
1411 debuga(_("cannot open %s for writing: %s\n"),wdir,strerror(errno));
1412 perror("SARG:");
1413 exit(EXIT_FAILURE);
1414 }
1415 time(&curtime);
1416 //strftime(wdir,sizeof(wdir),"%a %b %d %H:%M:%S %Z %Y",localtime(&curtime));
1417 loctm=localtime(&curtime);
1418 strftime(wdir,sizeof(wdir),"%Y-%m-%d %H:%M:%S",loctm);
1419 if (fprintf(fp_ou,"%s %d\n",wdir,loctm->tm_isdst)<0) {
1420 debuga(_("Failed to write the date in %s\n"),wdir);
1421 perror("SARG:");
1422 exit(EXIT_FAILURE);
1423 }
1424 if (fclose(fp_ou)==EOF) {
1425 debuga(_("Failed to write the date in %s: %s\n"),wdir,strerror(errno));
1426 exit(EXIT_FAILURE);
1427 }
1428
1429 copy_images();
1430 return(0);
1431 }
1432
1433 /*!
1434 Copy a string without overflowing the buffer. The copied string
1435 is properly terminated by an ASCII zero.
1436
1437 \param dest The destination buffer.
1438 \param src The source buffer.
1439 \param length The size of the destination buffer. The program is aborted
1440 if the length is negative or zero.
1441 */
1442 void safe_strcpy(char *dest,const char *src,int length)
1443 {
1444 if (length<=0) {
1445 debuga(_("Invalid buffer length passed to the function to safely copy a string\n"));
1446 exit(EXIT_FAILURE);
1447 }
1448 strncpy(dest,src,length-1);
1449 dest[length-1]='\0';
1450 }
1451
1452 void strip_latin(char *line)
1453 {
1454 int i,j;
1455 int skip;
1456
1457 j=0;
1458 skip=0;
1459 for (i=0;line[i];i++){
1460 if (skip){
1461 if (line[i]==';') skip=0;
1462 } else {
1463 if (line[i]=='&')
1464 skip=1;
1465 else
1466 line[j++]=line[i];
1467 }
1468 }
1469 line[j]='\0';
1470 return;
1471 }
1472
1473 void zdate(char *ftime,int ftimesize, char DateFormat)
1474 {
1475 time_t t;
1476 struct tm *local;
1477
1478 t = time(NULL);
1479 local = localtime(&t);
1480 if (DateFormat=='u')
1481 strftime(ftime, ftimesize, "%b/%d/%Y %H:%M", local);
1482 else if (DateFormat=='e')
1483 strftime(ftime, ftimesize, "%d/%b/%Y-%H:%M", local);
1484 else if (DateFormat=='w')
1485 strftime(ftime, ftimesize, "%W-%H-%M", local);
1486 return;
1487 }
1488
1489
1490 char *fixtime(long long int elap)
1491 {
1492 long int num = elap / 1000LL;
1493 int hor = 0;
1494 int min = 0;
1495 int sec = 0;
1496 static char buf[20];
1497
1498 hor=num / 3600L;
1499 min=(num % 3600L) / 60L;
1500 sec=num % 60L;
1501
1502 if(hor==0 && min==0 && sec==0)
1503 strcpy(buf,"0");
1504 else
1505 snprintf(buf,sizeof(buf),"%d:%02d:%02d",hor,min,sec);
1506
1507 return buf;
1508 }
1509
1510
1511 void date_from(char *date,int date_size, int *dfrom, int *duntil)
1512 {
1513 int d0=0;
1514 int m0=0;
1515 int y0=0;
1516 int d1=0;
1517 int m1=0;
1518 int y1=0;
1519
1520 if (isdigit(date[0])) {
1521 int next=-1;
1522
1523 if (sscanf(date,"%d/%d/%d%n",&d0,&m0,&y0,&next)!=3 || y0<100 || m0<1 || m0>12 || d0<1 || d0>31 || next<0) {
1524 debuga(_("The date passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1525 exit(EXIT_FAILURE);
1526 }
1527 if (date[next]=='-') {
1528 if (sscanf(date+next+1,"%d/%d/%d",&d1,&m1,&y1)!=3 || y1<100 || m1<1 || m1>12 || d1<1 || d1>31) {
1529 debuga(_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1530 exit(EXIT_FAILURE);
1531 }
1532 } else if (date[next]!='\0') {
1533 debuga(_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1534 exit(EXIT_FAILURE);
1535 } else {
1536 d1=d0;
1537 m1=m0;
1538 y1=y0;
1539 }
1540 } else {
1541 int i;
1542 time_t Today,t1;
1543 struct tm *Date0,Date1;
1544
1545 if (time(&Today)==(time_t)-1) {
1546 debuga(_("Failed to get the current time\n"));
1547 exit(EXIT_FAILURE);
1548 }
1549 if (sscanf(date,"day-%d",&i)==1) {
1550 if (i<0) {
1551 debuga(_("Invalid number of days in -d parameter\n"));
1552 exit(EXIT_FAILURE);
1553 }
1554 Today-=i*24*60*60;
1555 Date0=localtime(&Today);
1556 if (Date0==NULL) {
1557 debuga(_("Cannot convert local time: %s\n"),strerror(errno));
1558 exit(EXIT_FAILURE);
1559 }
1560 y0=y1=Date0->tm_year+1900;
1561 m0=m1=Date0->tm_mon+1;
1562 d0=d1=Date0->tm_mday;
1563 } else if (sscanf(date,"week-%d",&i)==1) {
1564 /*
1565 There is no portable way to find the first day of the week even though the
1566 information is available in the locale. nl_langinfo has the unofficial
1567 parameters _NL_TIME_FIRST_WEEKDAY and _NL_TIME_WEEK_1STDAY but they are
1568 undocumented as is their return value and it is discouraged to use them.
1569 Beside, nl_langinfo isn't available on windows and the first day of the
1570 week isn't available at all on that system.
1571 */
1572 const int FirstWeekDay=1;
1573 time_t WeekBegin;
1574
1575 if (i<0) {
1576 debuga(_("Invalid number of weeks in -d parameter\n"));
1577 exit(EXIT_FAILURE);
1578 }
1579 Date0=localtime(&Today);
1580 if (Date0==NULL) {
1581 debuga(_("Cannot convert local time: %s\n"),strerror(errno));
1582 exit(EXIT_FAILURE);
1583 }
1584 WeekBegin=Today-((Date0->tm_wday-FirstWeekDay+7)%7)*24*60*60;
1585 WeekBegin-=i*7*24*60*60;
1586 Date0=localtime(&WeekBegin);
1587 if (Date0==NULL) {
1588 debuga(_("Cannot convert local time: %s\n"),strerror(errno));
1589 exit(EXIT_FAILURE);
1590 }
1591 y0=Date0->tm_year+1900;
1592 m0=Date0->tm_mon+1;
1593 d0=Date0->tm_mday;
1594 WeekBegin+=6*24*60*60;
1595 Date0=localtime(&WeekBegin);
1596 if (Date0==NULL) {
1597 debuga(_("Cannot convert local time: %s\n"),strerror(errno));
1598 exit(EXIT_FAILURE);
1599 }
1600 y1=Date0->tm_year+1900;
1601 m1=Date0->tm_mon+1;
1602 d1=Date0->tm_mday;
1603 } else if (sscanf(date,"month-%d",&i)==1) {
1604 if (i<0) {
1605 debuga(_("Invalid number of months in -d parameter\n"));
1606 exit(EXIT_FAILURE);
1607 }
1608 Date0=localtime(&Today);
1609 if (Date0==NULL) {
1610 debuga(_("Cannot convert local time: %s\n"),strerror(errno));
1611 exit(EXIT_FAILURE);
1612 }
1613 if (Date0->tm_mon<i%12) {
1614 y0=Date0->tm_year+1900-i/12-1;
1615 m0=(Date0->tm_mon+12-i%12)%12+1;
1616 d0=1;
1617 } else {
1618 y0=Date0->tm_year+1900-i/12;
1619 m0=Date0->tm_mon-i%12+1;
1620 d0=1;
1621 }
1622 memcpy(&Date1,Date0,sizeof(struct tm));
1623 Date1.tm_isdst=-1;
1624 Date1.tm_mday=1;
1625 if (m0<12) {
1626 Date1.tm_mon=m0;
1627 Date1.tm_year=y0-1900;
1628 } else {
1629 Date1.tm_mon=0;
1630 Date1.tm_year=y0-1900+1;
1631 }
1632 t1=mktime(&Date1);
1633 t1-=24*60*60;
1634 Date0=localtime(&t1);
1635 y1=Date0->tm_year+1900;
1636 m1=Date0->tm_mon+1;
1637 d1=Date0->tm_mday;
1638 } else {
1639 debuga(_("Invalid date range passed on command line\n"));
1640 exit(EXIT_FAILURE);
1641 }
1642 }
1643
1644 *dfrom=y0*10000+m0*100+d0;
1645 *duntil=y1*10000+m1*100+d1;
1646 snprintf(date,date_size,"%02d/%02d/%04d-%02d/%02d/%04d",d0,m0,y0,d1,m1,y1);
1647 return;
1648 }
1649
1650
1651 char *strlow(char *string)
1652 {
1653 char *s;
1654
1655 if (string)
1656 {
1657 for (s = string; *s; ++s)
1658 *s = tolower(*s);
1659 }
1660
1661 return string;
1662 }
1663
1664
1665
1666
1667 char *strup(char *string)
1668 {
1669 char *s;
1670
1671 if (string)
1672 {
1673 for (s = string; *s; ++s)
1674 *s = toupper(*s);
1675 }
1676
1677 return string;
1678 }
1679
1680
1681 void removetmp(const char *outdir)
1682 {
1683 FILE *fp_gen;
1684 char filename[256];
1685
1686 if(!RemoveTempFiles)
1687 return;
1688
1689 if(debug) {
1690 debuga(_("Purging temporary file sarg-general\n"));
1691 }
1692 if (snprintf(filename,sizeof(filename),"%s/sarg-general",outdir)>=sizeof(filename)) {
1693 debuga(_("(removetmp) directory too long to remove %s/sarg-period\n"),outdir);
1694 exit(EXIT_FAILURE);
1695 }
1696 if((fp_gen=fopen(filename,"w"))==NULL){
1697 debuga(_("(removetmp) Cannot open file %s: %s\n"),filename,strerror(errno));
1698 exit(EXIT_FAILURE);
1699 }
1700 totalger(fp_gen,filename);
1701 if (fclose(fp_gen)==EOF) {
1702 debuga(_("Failed to close %s after writing the total line: %s\n"),filename,strerror(errno));
1703 exit(EXIT_FAILURE);
1704 }
1705 }
1706
1707 void load_excludecodes(const char *ExcludeCodes)
1708 {
1709 FILE *fp_in;
1710 char data[80];
1711 int i;
1712 int Stored;
1713 long int MemSize;
1714
1715 if(ExcludeCodes[0] == '\0')
1716 return;
1717
1718 if((fp_in=fopen(ExcludeCodes,"r"))==NULL) {
1719 debuga(_("(util) Cannot open file %s (exclude_codes): %s\n"),ExcludeCodes,strerror(errno));
1720 exit(EXIT_FAILURE);
1721 }
1722
1723 if (fseek(fp_in, 0, SEEK_END)==-1) {
1724 debuga(_("Failed to move till the end of the excluded codes file %s: %s\n"),ExcludeCodes,strerror(errno));
1725 exit(EXIT_FAILURE);
1726 }
1727 MemSize = ftell(fp_in);
1728 if (MemSize<0) {
1729 debuga(_("Cannot get the size of file %s\n"),ExcludeCodes);
1730 exit(EXIT_FAILURE);
1731 }
1732 if (fseek(fp_in, 0, SEEK_SET)==-1) {
1733 debuga(_("Failed to rewind the excluded codes file %s: %s\n"),ExcludeCodes,strerror(errno));
1734 exit(EXIT_FAILURE);
1735 }
1736
1737 MemSize+=1;
1738 if((excludecode=(char *) malloc(MemSize))==NULL) {
1739 debuga(_("malloc error (%ld)\n"),MemSize);
1740 exit(EXIT_FAILURE);
1741 }
1742 memset(excludecode,0,MemSize);
1743
1744 Stored=0;
1745 while(fgets(data,sizeof(data),fp_in)!=NULL) {
1746 if (data[0]=='#') continue;
1747 for (i=strlen(data)-1 ; i>=0 && (unsigned char)data[i]<=' ' ; i--) data[i]='\0';
1748 if (i<0) continue;
1749 if (Stored+i+2>=MemSize) {
1750 debuga(_("Too many codes to exclude in file %s\n"),ExcludeCodes);
1751 break;
1752 }
1753 strcat(excludecode,data);
1754 strcat(excludecode,";");
1755 Stored+=i+1;
1756 }
1757
1758 fclose(fp_in);
1759 return;
1760 }
1761
1762 void free_excludecodes(void)
1763 {
1764 if (excludecode) {
1765 free(excludecode);
1766 excludecode=NULL;
1767 }
1768 }
1769
1770 int vercode(const char *code)
1771 {
1772 char *cod;
1773 int clen;
1774
1775 if (excludecode && excludecode[0]!='\0') {
1776 clen=strlen(code);
1777 cod=excludecode;
1778 while (cod) {
1779 if (strncmp(code,cod,clen)==0 && cod[clen]==';')
1780 return 1;
1781 cod=strchr(cod,';');
1782 if (cod) cod++;
1783 }
1784 }
1785 return 0;
1786 }
1787
1788 void fixnone(char *str)
1789 {
1790 int i;
1791
1792 for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--);
1793 if(i==3 && strncmp(str,"none",4) == 0)
1794 str[0]='\0';
1795
1796 return;
1797 }
1798
1799 void fixendofline(char *str)
1800 {
1801 int i;
1802
1803 for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--) str[i]=0;
1804 }
1805
1806 #ifdef LEGACY_TESTVALIDUSERCHAR
1807 int testvaliduserchar(const char *user)
1808 {
1809 int x=0;
1810 int y=0;
1811
1812 for (y=0; y<strlen(UserInvalidChar); y++) {
1813 for (x=0; x<strlen(user); x++) {
1814 if(user[x] == UserInvalidChar[y])
1815 return 1;
1816 }
1817 }
1818 return 0;
1819 }
1820 #else
1821 int testvaliduserchar(const char *user)
1822 {
1823 char * p_UserInvalidChar = UserInvalidChar ;
1824 const char * p_user ;
1825
1826 while( *p_UserInvalidChar ) {
1827 p_user = user ;
1828 while ( *p_user ) {
1829 if( *p_UserInvalidChar == *p_user )
1830 return 1;
1831 p_user++ ;
1832 }
1833 p_UserInvalidChar++ ;
1834 }
1835 return 0;
1836 }
1837 #endif
1838
1839 int compar( const void *a, const void *b )
1840 {
1841 if( *(int *)a > *(int *)b ) return 1;
1842 if( *(int *)a < *(int *)b ) return -1;
1843 return 0;
1844 }
1845
1846 int getnumlist( char *buf, numlist *list, const int len, const int maxvalue )
1847 {
1848 int i, j, d, flag, r1, r2;
1849 char *pbuf, **bp, *strbufs[ 24 ];
1850
1851 bp = strbufs;
1852 strtok( buf, " \t" );
1853 for( *bp = strtok( NULL, "," ), list->len = 0; *bp; *bp = strtok( NULL, "," ) ) {
1854 if( ++bp >= &strbufs[ 24 ] )
1855 break;
1856 list->len++;
1857 }
1858 if( ! list->len )
1859 return -1;
1860 d = 0;
1861 for( i = 0; i < list->len; i++ ) {
1862 if( strchr( strbufs[ i ], '-' ) != 0 ) {
1863 pbuf = strbufs[ i ];
1864 strtok( pbuf, "-" );
1865 pbuf = strtok( NULL, "\0" );
1866 r1 = atoi( strbufs[ i ] );
1867 if( ( r2 = atoi( pbuf ) ) >= maxvalue || r1 >= r2 )
1868 return -1;
1869 if( i + d + ( r2 - r1 ) + 1 <= len ) {
1870 for( j = r1; j <= r2; j++ )
1871 list->list[ i + d++ ] = j;
1872 d--;
1873 }
1874 }
1875 else
1876 if( ( list->list[ i + d ] = atoi( strbufs[ i ] ) ) >= maxvalue )
1877 return 1;
1878 }
1879 list->len += d;
1880 qsort( list->list, list->len, sizeof( int ), compar );
1881 do {
1882 flag = 0;
1883 for( i = 0; i < list->len - 1; i++ )
1884 if( list->list[ i ] == list->list[ i + 1 ] ) {
1885 for( j = i + 1; j < list->len; j++ )
1886 list->list[ j - 1 ] = list->list[ j ];
1887 list->len--;
1888 flag = 1;
1889 break;
1890 }
1891 } while( flag );
1892 return 0;
1893 }
1894
1895
1896 char *get_size(const char *path, const char *file)
1897 {
1898 FILE *fp;
1899 static char response[255];
1900 char cmd[255];
1901 char *ptr;
1902
1903 if (snprintf(cmd,sizeof(cmd),"du -skh %s%s",path,file)>=sizeof(cmd)) {
1904 debuga(_("Cannot get disk space because the path %s%s is too long\n"),path,file);
1905 exit(EXIT_FAILURE);
1906 }
1907 if ((fp = popen(cmd, "r")) == NULL) {
1908 debuga(_("Cannot get disk space with command %s\n"),cmd);
1909 exit(EXIT_FAILURE);
1910 }
1911 if (!fgets(response, sizeof(response), fp)) {
1912 debuga(_("Cannot get disk size with command %s\n"),cmd);
1913 exit(EXIT_FAILURE);
1914 }
1915 ptr=strchr(response,'\t');
1916 if (ptr==NULL) {
1917 debuga(_("The command %s failed\n"),cmd);
1918 exit(EXIT_FAILURE);
1919 }
1920 pclose(fp);
1921 *ptr='\0';
1922
1923 return (response);
1924 }
1925
1926 void show_info(FILE *fp_ou)
1927 {
1928 char ftime[127];
1929
1930 if(!ShowSargInfo) return;
1931 zdate(ftime, sizeof(ftime), df);
1932 fputs("<div class=\"info\">",fp_ou);
1933 fprintf(fp_ou,_("Generated by <a href='%s'>%s-%s</a> on %s"),URL,PGM,VERSION,ftime);
1934 fputs("</div>\n",fp_ou);
1935 }
1936
1937 void show_sarg(FILE *fp_ou, int depth)
1938 {
1939 int i;
1940
1941 if(!ShowSargLogo) return;
1942 fputs("<div class=\"logo\"><a href=\"http://sarg.sourceforge.net\"><img src=\"",fp_ou);
1943 for (i=0 ; i<depth ; i++)
1944 fputs("../",fp_ou);
1945 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);
1946 }
1947
1948 void write_logo_image(FILE *fp_ou)
1949 {
1950 if(LogoImage[0]!='\0')
1951 fprintf(fp_ou, "<div class=\"logo\"><img src=\"%s\" width=\"%s\" height=\"%s\" alt=\"Logo\">&nbsp;%s</div>\n",LogoImage,Width,Height,LogoText);
1952 }
1953
1954 void write_html_head(FILE *fp_ou, int depth, const char *page_title,int javascript)
1955 {
1956 int i;
1957
1958 fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n",fp_ou);
1959 fprintf(fp_ou, "<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet);
1960 if (page_title) fprintf(fp_ou,"<title>%s</title>\n",page_title);
1961 css(fp_ou);
1962 if ((javascript & HTML_JS_SORTTABLE)!=0 && SortTableJs[0]) {
1963 fputs("<script type=\"text/javascript\" src=\"",fp_ou);
1964 if (strncmp(SortTableJs,"../",3)==0) {
1965 for (i=0 ; i<depth ; i++) fputs("../",fp_ou);
1966 }
1967 fputs(SortTableJs,fp_ou);
1968 fputs("\"></script>\n",fp_ou);
1969 }
1970 fputs("</head>\n<body>\n",fp_ou);
1971 }
1972
1973 void write_html_header(FILE *fp_ou, int depth, const char *page_title,int javascript)
1974 {
1975 write_html_head(fp_ou,depth,page_title,javascript);
1976 write_logo_image(fp_ou);
1977 show_sarg(fp_ou, depth);
1978 fprintf(fp_ou,"<div class=\"title\"><table cellpadding=\"0\" cellspacing=\"0\">\n<tr><th class=\"title_c\">%s</th></tr>\n",Title);
1979 }
1980
1981 void close_html_header(FILE *fp_ou)
1982 {
1983 fputs("</table></div>\n",fp_ou);
1984 }
1985
1986 int write_html_trailer(FILE *fp_ou)
1987 {
1988 show_info(fp_ou);
1989 if (fputs("</body>\n</html>\n",fp_ou)==EOF) return(-1);
1990 return(0);
1991 }
1992
1993 void output_html_string(FILE *fp_ou,const char *str,int maxlen)
1994 {
1995 int i=0;
1996
1997 while (*str && (maxlen<=0 || i<maxlen)) {
1998 switch (*str) {
1999 case '&':
2000 fputs("&amp;",fp_ou);
2001 break;
2002 case '<':
2003 fputs("&lt;",fp_ou);
2004 break;
2005 case '>':
2006 fputs("&gt;",fp_ou);
2007 break;
2008 case '"':
2009 fputs("&quot;",fp_ou);
2010 break;
2011 case '\'':
2012 fputs("&#39;",fp_ou);
2013 break;
2014 default:
2015 fputc(*str,fp_ou);
2016 }
2017 str++;
2018 i++;
2019 }
2020 if (maxlen>0 && i>=maxlen)
2021 fputs("&hellip;",fp_ou);
2022 }
2023
2024 void output_html_url(FILE *fp_ou,const char *url)
2025 {
2026 while (*url) {
2027 if (*url=='&')
2028 fputs("&amp;",fp_ou);
2029 else
2030 fputc(*url,fp_ou);
2031 url++;
2032 }
2033 }
2034
2035 /*!
2036 Write a host name inside an A tag of a HTML file. If the host name starts
2037 with a star, it is assumed to be an alias that cannot be put inside a link
2038 so the A tag is not written around the host name.
2039
2040 \param fp_ou The handle of the HTML file.
2041 \param url The host to display in the HTML file.
2042 \param maxlen The maximum number of characters to print into the host name.
2043 */
2044 void output_html_link(FILE *fp_ou,const char *url,int maxlen)
2045 {
2046 if (url[0]==ALIAS_PREFIX) {
2047 // this is an alias, no need for a A tag
2048 output_html_string(fp_ou,url+1,100);
2049 } else {
2050 if (skip_scheme(url)==url)
2051 fputs("<a href=\"http://",fp_ou);//no scheme in the url, assume http:// to make the link clickable
2052 else
2053 fputs("<a href=\"",fp_ou);//the scheme is in the url, no need to add one
2054 output_html_url(fp_ou,url);
2055 fputs("\">",fp_ou);
2056 output_html_string(fp_ou,url,100);
2057 fputs("</a>",fp_ou);
2058 }
2059 }
2060
2061 void url_module(const char *url, char *w2)
2062 {
2063 int x, y;
2064 char w[255];
2065
2066 y=0;
2067 for(x=strlen(url)-1; x>=0; x--) {
2068 if(url[x] == '/' || y>=sizeof(w)-1) break;
2069 w[y++]=url[x];
2070 }
2071 if (x<0) {
2072 w2[0]='\0';
2073 return;
2074 }
2075
2076 x=0;
2077 for(y=y-1; y>=0; y--) {
2078 w2[x++]=w[y];
2079 }
2080 w2[x]='\0';
2081 }
2082
2083 /*!
2084 Mangle an URL to produce a part that can be used as an anchor in
2085 a html <a name=""> tag.
2086
2087 \param url The URL to mangle.
2088 \param anchor The buffer to write the mangled URL.
2089 \param size The size of the buffer.
2090 */
2091 void url_to_anchor(const char *url,char *anchor,int size)
2092 {
2093 int i,j;
2094 bool skip;
2095
2096 // find url end
2097 for (i=0 ; url[i] && url[i]!='/' && url[i]!='?' ; i++);
2098 i--;
2099 if (i<=0) {
2100 anchor[0]='\0';
2101 return;
2102 }
2103
2104 // only keep really safe characters
2105 skip=false;
2106 j=size-1;
2107 anchor[j]='\0';
2108 while (j>0 && i>=0)
2109 {
2110 if(isalnum(url[i]) || url[i]=='-' || url[i]=='_' || url[i]=='.') {
2111 anchor[--j]=url[i];
2112 skip=false;
2113 } else {
2114 if (!skip) anchor[--j]='_';
2115 skip=true;
2116 }
2117 i--;
2118 }
2119 if (j>0)
2120 {
2121 while ( anchor[j])
2122 {
2123 *anchor=anchor[j];
2124 anchor++;
2125 }
2126 *anchor='\0';
2127 }
2128 }
2129
2130 void version(void)
2131 {
2132 printf(_("SARG Version: %s\n"),VERSION);
2133 exit(EXIT_SUCCESS);
2134 }
2135
2136 char *get_param_value(const char *param,char *line)
2137 {
2138 int plen;
2139
2140 while (*line==' ' || *line=='\t') line++;
2141 plen=strlen(param);
2142 if (strncasecmp(line,param,plen)) return(NULL);
2143 if (line[plen]!=' ' && line[plen]!='\t') return(NULL);
2144 line+=plen;
2145 while (*line==' ' || *line=='\t') line++;
2146 return(line);
2147 }
2148
2149 void unlinkdir(const char *dir,bool contentonly)
2150 {
2151 struct stat st;
2152 DIR *dirp;
2153 struct dirent *direntp;
2154 char dname[MAXLEN];
2155 int err;
2156
2157 dirp=opendir(dir);
2158 if (!dirp) return;
2159 while ((direntp = readdir(dirp)) != NULL) {
2160 if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
2161 (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
2162 continue;
2163 if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
2164 debuga(_("directory name to delete too long: %s/%s\n"),dir,direntp->d_name);
2165 exit(EXIT_FAILURE);
2166 }
2167 #ifdef HAVE_LSTAT
2168 err=lstat(dname,&st);
2169 #else
2170 err=stat(dname,&st);
2171 #endif
2172 if (err) {
2173 debuga(_("cannot stat %s\n"),dname);
2174 exit(EXIT_FAILURE);
2175 }
2176 if (S_ISREG(st.st_mode)) {
2177 if (unlink(dname)) {
2178 debuga(_("Cannot delete \"%s\": %s\n"),dname,strerror(errno));
2179 exit(EXIT_FAILURE);
2180 }
2181 } else if (S_ISDIR(st.st_mode)) {
2182 unlinkdir(dname,0);
2183 } else {
2184 debuga(_("unknown path type %s\n"),dname);
2185 }
2186 }
2187 closedir(dirp);
2188
2189 if (!contentonly) {
2190 if (rmdir(dir)) {
2191 debuga(_("Cannot delete \"%s\": %s\n"),dir,strerror(errno));
2192 exit(EXIT_FAILURE);
2193 }
2194 }
2195 }
2196
2197 /*!
2198 Delete every file from the temporary directory where sarg is told to store its
2199 temporary files.
2200
2201 As any stray file left over by a previous run would be included in the report, we
2202 must delete every file from the temporary directory before we start processing the logs.
2203
2204 But the temporary directory is given by the user either in the configuration file or
2205 on the command line. We check that the user didn't give a wrong directory by looking
2206 at the files stored in the directory. If a single file is not one of ours, we abort.
2207
2208 \param dir The temporary directory to purge.
2209 */
2210 void emptytmpdir(const char *dir)
2211 {
2212 struct stat st;
2213 DIR *dirp;
2214 struct dirent *direntp;
2215 int dlen;
2216 int elen;
2217 char dname[MAXLEN];
2218 int err;
2219 int i;
2220 static const char *TmpExt[]=
2221 {
2222 ".int_unsort",
2223 ".int_log",
2224 ".day",
2225 "htmlrel.txt",
2226 ".user_unsort",
2227 ".user_log",
2228 ".utmp",
2229 ".ip",
2230 "lastlog1",
2231 "lastlog"
2232 };
2233
2234 dirp=opendir(dir);
2235 if (!dirp) return;
2236
2237 // make sure the temporary directory contains only our files
2238 while ((direntp = readdir(dirp)) != NULL) {
2239 if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
2240 (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
2241 continue;
2242
2243 // is it one of our files
2244 dlen=strlen(direntp->d_name);
2245 for (i=sizeof(TmpExt)/sizeof(TmpExt[0])-1 ; i>=0 ; i--) {
2246 elen=strlen(TmpExt[i]);
2247 if (dlen>=elen && strcasecmp(direntp->d_name+dlen-elen,TmpExt[i])==0) break;
2248 }
2249 if (i<0) {
2250 debuga(_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2251 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2252 "directory or manually delete the content of \"%s\"\n"),direntp->d_name,dir,dir);
2253 exit(EXIT_FAILURE);
2254 }
2255
2256 if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
2257 debuga(_("directory name to delete too long: %s/%s\n"),dir,direntp->d_name);
2258 exit(EXIT_FAILURE);
2259 }
2260
2261 #ifdef HAVE_LSTAT
2262 err=lstat(dname,&st);
2263 #else
2264 err=stat(dname,&st);
2265 #endif
2266 if (err) {
2267 debuga(_("cannot stat \"%s\"\n"),dname);
2268 exit(EXIT_FAILURE);
2269 }
2270 if (S_ISDIR(st.st_mode)) {
2271 unlinkdir(dname,0);
2272 } else if (!S_ISREG(st.st_mode)) {
2273 debuga(_("Unknown path type for \"%s\". Check temporary directory\n"),dname);
2274 exit(EXIT_FAILURE);
2275 }
2276 }
2277 rewinddir(dirp);
2278
2279 // now delete our files
2280 while ((direntp = readdir(dirp)) != NULL) {
2281 if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
2282 (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
2283 continue;
2284
2285 // is it one of our files
2286 dlen=strlen(direntp->d_name);
2287 for (i=sizeof(TmpExt)/sizeof(TmpExt[0])-1 ; i>=0 ; i--) {
2288 elen=strlen(TmpExt[i]);
2289 if (dlen>=elen && strcasecmp(direntp->d_name+dlen-elen,TmpExt[i])==0) break;
2290 }
2291 if (i<0) {
2292 debuga(_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2293 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2294 "directory or manually delete the content of \"%s\"\n"),direntp->d_name,dir,dir);
2295 exit(EXIT_FAILURE);
2296 }
2297
2298 if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
2299 debuga(_("directory name to delete too long: %s/%s\n"),dir,direntp->d_name);
2300 exit(EXIT_FAILURE);
2301 }
2302 #ifdef HAVE_LSTAT
2303 err=lstat(dname,&st);
2304 #else
2305 err=stat(dname,&st);
2306 #endif
2307 if (err) {
2308 debuga(_("cannot stat \"%s\"\n"),dname);
2309 exit(EXIT_FAILURE);
2310 }
2311 if (S_ISREG(st.st_mode)) {
2312 if (unlink(dname)) {
2313 debuga(_("Cannot delete \"%s\": %s\n"),dname,strerror(errno));
2314 exit(EXIT_FAILURE);
2315 }
2316 } else {
2317 debuga(_("unknown path type %s\n"),dname);
2318 }
2319 }
2320 closedir(dirp);
2321 }
2322
2323 /*!
2324 Extract an url, IPv4 or IPv6 from a buffer. The IP addresses may end with a
2325 prefix size.
2326
2327 \param buf The buffer to parse.
2328 \param text A pointer to set to the beginning of the string pattern. No terminating zero is inserted.
2329 The pointer may be NULL.
2330 \param ipv4 A 4 bytes buffer to store the bytes of the IPv4 address.
2331 \param ipv6 A 8 short integers buffer to store the values of the IPv6 address.
2332 \param nbits The number of prefix bits for an IP address.
2333 \param next The content of the line after the extracted address.
2334
2335 \retval 3 The pattern is a IPv6 address.
2336 \retval 2 The pattern is a IPv4 address.
2337 \retval 1 The patter is a string.
2338 \retval 0 Empty pattern.
2339 */
2340 int extract_address_mask(const char *buf,const char **text,unsigned char *ipv4,unsigned short int *ipv6,int *nbits,const char **next)
2341 {
2342 int i;
2343 int j;
2344 int ip_size;
2345 unsigned int value4, value6;
2346 unsigned short int addr[8];
2347 int addr_len;
2348 int nibble6_len;
2349 int mask, max_mask;
2350 int pad_pos;
2351 int pad_len;
2352 bool bracket=false;
2353 bool port=false;
2354 bool port_num=0;
2355
2356 // skip leading spaces and tabs
2357 while (*buf && (*buf==' ' || *buf=='\t')) buf++;
2358
2359 // find out the nature of the pattern
2360 ip_size=0x60 | 0x04;
2361 if (*buf=='[') {
2362 bracket=true;
2363 ip_size=0x60;
2364 buf++;
2365 }
2366 value4=0U;
2367 value6=0U;
2368 addr_len=0;
2369 nibble6_len=0;
2370 pad_pos=-1;
2371 for (i=0 ; (unsigned char)buf[i]>' ' && buf[i]!='/' && buf[i]!='?' && (!bracket || buf[i]!=']') && ip_size ; i++) {
2372 if (ip_size & 0x04) {
2373 if (isdigit(buf[i])) {
2374 if (port) {
2375 port_num=port_num*10+(buf[i]-'0');
2376 if (port_num>65535) ip_size&=~0x04;
2377 } else {
2378 value4=value4*10+(buf[i]-'0');
2379 if (value4>0xFFU) ip_size&=~0x04;
2380 }
2381 } else if (buf[i]=='.' && addr_len<4) {
2382 addr[addr_len++]=(unsigned short)(value4 & 0xFFU);
2383 value4=0U;
2384 } else if (!port && buf[i]==':') {
2385 port=true;
2386 } else {
2387 ip_size&=~0x04;
2388 }
2389 }
2390 if (ip_size & 0x60) {
2391 if (isdigit(buf[i])) {
2392 value6=(value6<<4)+(buf[i]-'0');
2393 nibble6_len++;
2394 if (value6>0xFFFFU) ip_size&=~0x60;
2395 } else if (toupper(buf[i])>='A' && toupper(buf[i])<='F') {
2396 value6=(value6<<4)+(toupper(buf[i])-'A'+10);
2397 nibble6_len++;
2398 if (value6>0xFFFFU) ip_size&=~0x60;
2399 } else if (buf[i]==':' && addr_len<8) {
2400 if (nibble6_len>0) {
2401 addr[addr_len++]=(unsigned short)(value6 & 0xFFFFU);
2402 nibble6_len=0;
2403 }
2404 value6=0U;
2405 if (buf[i+1]==':') {
2406 pad_pos=addr_len;
2407 i++;
2408 }
2409 } else {
2410 ip_size&=~0x60;
2411 }
2412 }
2413 }
2414 if (i==0) return(0);
2415 if (ip_size & 0x04) {
2416 if (addr_len!=3)
2417 ip_size&=~0x04;
2418 else
2419 addr[addr_len++]=(unsigned short)(value4 & 0xFFU);
2420 }
2421 if (ip_size & 0x60) {
2422 if (pad_pos<0 && addr_len!=7) {
2423 ip_size&=~0x60;
2424 } else if (pad_pos>=0 && addr_len>=7)
2425 ip_size&=~0x60;
2426 else if (nibble6_len>0)
2427 addr[addr_len++]=(unsigned short)(value6 & 0xFFFFU);
2428 }
2429 if (!ip_size) {
2430 if (text) {
2431 *text=buf;
2432 if (bracket) (*text)--;
2433 }
2434 while ((unsigned char)buf[i]>' ') i++;
2435 if (next) *next=buf+i;
2436 return(1);
2437 }
2438 max_mask=(ip_size & 0x04) ? 4*8 : 8*16;
2439 if (buf[i]=='/') {
2440 i++;
2441 mask=atoi(buf+i);
2442 while (isdigit(buf[i])) i++;
2443 if (mask<0 || mask>max_mask) mask=max_mask;
2444 } else
2445 mask=max_mask;
2446 if (ip_size & 0x60 && bracket && buf[i]==']') i++;
2447 if (next) *next=buf+i;
2448 if (ip_size & 0x04) {
2449 if (nbits) *nbits=mask;
2450 for (i=0 ; i<addr_len ; i++)
2451 ipv4[i]=(unsigned char)addr[i];
2452 return(2);
2453 }
2454
2455 // IPv6 address
2456 if (nbits) *nbits=mask;
2457 i=0;
2458 j=0;
2459 if (pad_pos>=0) {
2460 while (i<pad_pos)
2461 ipv6[j++]=(unsigned short int)addr[i++];
2462 pad_len=8-addr_len;
2463 while (j<pad_pos+pad_len)
2464 ipv6[j++]=0;
2465 }
2466 while (i<addr_len)
2467 ipv6[j++]=(unsigned short int)addr[i++];
2468 return(3);
2469 }