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