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