]> git.ipfire.org Git - thirdparty/sarg.git/blob - util.c
Make b-tree cache functions static.
[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 /*!
1044 Fill the period with the specified range.
1045
1046 \param period The period to change.
1047 \param dfrom The start date in the form year*10000+month*100+day.
1048 \param duntil The end date in the form year*10000+month*100+day.
1049 */
1050 void getperiod_fromrange(struct periodstruct *period,int dfrom,int duntil)
1051 {
1052 memset(&period->start,0,sizeof(period->start));
1053 period->start.tm_mday=dfrom%100;
1054 period->start.tm_mon=(dfrom/100)%100-1;
1055 period->start.tm_year=(dfrom/10000)-1900;
1056
1057 memset(&period->end,0,sizeof(period->end));
1058 period->end.tm_mday=duntil%100;
1059 period->end.tm_mon=(duntil/100)%100-1;
1060 period->end.tm_year=(duntil/10000)-1900;
1061 }
1062
1063 /*!
1064 Get the range from a period.
1065
1066 \param period The period to convert to a range.
1067 \param dfrom The variable to store the range beginning. It can be NULL.
1068 \param duntil The variable to store the range end. It can be NULL.
1069 */
1070 void getperiod_torange(const struct periodstruct *period,int *dfrom,int *duntil)
1071 {
1072 if (dfrom)
1073 *dfrom=(period->start.tm_year+1900)*10000+(period->start.tm_mon+1)*100+period->start.tm_mday;
1074 if (duntil)
1075 *duntil=(period->end.tm_year+1900)*10000+(period->end.tm_mon+1)*100+period->end.tm_mday;
1076 }
1077
1078 /*!
1079 Update the \a main period to encompass the period in \a candidate.
1080 */
1081 void getperiod_merge(struct periodstruct *main,struct periodstruct *candidate)
1082 {
1083 int cdate;
1084 int mdate;
1085
1086 mdate=(main->start.tm_year)*10000+(main->start.tm_mon)*100+main->start.tm_mday;
1087 cdate=(candidate->start.tm_year)*10000+(candidate->start.tm_mon)*100+candidate->start.tm_mday;
1088 if (cdate<mdate) memcpy(&main->start,&candidate->start,sizeof(struct tm));
1089
1090 mdate=(main->end.tm_year)*10000+(main->end.tm_mon)*100+main->end.tm_mday;
1091 cdate=(candidate->end.tm_year)*10000+(candidate->end.tm_mon)*100+candidate->end.tm_mday;
1092 if (cdate>mdate) memcpy(&main->end,&candidate->end,sizeof(struct tm));
1093 }
1094
1095 int getperiod_buildtext(struct periodstruct *period)
1096 {
1097 int i;
1098 int range;
1099 char text1[40], text2[40];
1100
1101 if (df=='u') {
1102 i=strftime(text1, sizeof(text1), "%Y %b %d", &period->start);
1103 } else if(df=='e') {
1104 i=strftime(text1, sizeof(text1), "%d %b %Y", &period->start);
1105 } else /*if (df=='w')*/ {
1106 IndexTree=INDEX_TREE_FILE;
1107 i=strftime(text1, sizeof(text1), "%Y.%U", &period->start);
1108 }
1109 if (i == 0) return(-1);
1110
1111 range=(period->start.tm_year!=period->end.tm_year ||
1112 period->start.tm_mon!=period->end.tm_mon ||
1113 period->start.tm_mday!=period->end.tm_mday);
1114 if (range) {
1115 if (df=='u') {
1116 i=strftime(text2, sizeof(text2)-i, "%Y %b %d", &period->end);
1117 } else if (df=='e') {
1118 i=strftime(text2, sizeof(text2)-i, "%d %b %Y", &period->end);
1119 } else {
1120 i=strftime(text2, sizeof(text2)-i, "%Y.%U", &period->end);
1121 }
1122 if (i == 0) return(-1);
1123 }
1124
1125 if (range) {
1126 snprintf(period->text,sizeof(period->text),"%s-%s",text1,text2);
1127 snprintf(period->html,sizeof(period->html),"%s&mdash;%s",text1,text2);
1128 } else {
1129 safe_strcpy(period->text,text1,sizeof(period->text));
1130 safe_strcpy(period->html,text1,sizeof(period->html));
1131 }
1132 return(0);
1133 }
1134
1135 static void copy_images(void)
1136 {
1137 FILE *img_in, *img_ou;
1138 char images[512];
1139 char srcfile[MAXLEN];
1140 char dstfile[MAXLEN];
1141 DIR *dirp;
1142 struct dirent *direntp;
1143 char buffer[MAXLEN];
1144 size_t nread;
1145 struct stat info;
1146
1147 if (snprintf(images,sizeof(images),"%simages",outdir)>=sizeof(images)) {
1148 debuga(__FILE__,__LINE__,_("Cannot copy images to target directory %simages\n"),outdir);
1149 exit(EXIT_FAILURE);
1150 }
1151 if (access(images,R_OK)!=0) {
1152 if (PortableMkDir(images,0755)) {
1153 debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),images,strerror(errno));
1154 exit(EXIT_FAILURE);
1155 }
1156 }
1157
1158 dirp = opendir(ImageDir);
1159 if(dirp==NULL) {
1160 debuga(__FILE__,__LINE__,_("Cannot open directory \"%s\": %s\n"),ImageDir,strerror(errno));
1161 return;
1162 }
1163 while ((direntp = readdir( dirp )) != NULL ){
1164 if(direntp->d_name[0]=='.')
1165 continue;
1166 if (snprintf(srcfile,sizeof(srcfile),"%s/%s",ImageDir,direntp->d_name)>=sizeof(srcfile)) {
1167 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
1168 debuga_more("%s/%s",ImageDir,direntp->d_name);
1169 exit(EXIT_FAILURE);
1170 }
1171 if (stat(srcfile,&info)) {
1172 debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),srcfile,strerror(errno));
1173 continue;
1174 }
1175 if (S_ISREG(info.st_mode)) {
1176 if (snprintf(dstfile,sizeof(dstfile),"%s/%s",images,direntp->d_name)>=sizeof(dstfile)) {
1177 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
1178 debuga_more("%s/%s",images,direntp->d_name);
1179 exit(EXIT_FAILURE);
1180 }
1181 img_in = fopen(srcfile, "rb");
1182 if(img_in!=NULL) {
1183 img_ou = fopen(dstfile, "wb");
1184 if(img_ou!=NULL) {
1185 while ((nread = fread(buffer,1,sizeof(buffer),img_in))>0) {
1186 if (fwrite(buffer,1,nread,img_ou)!=nread) {
1187 debuga(__FILE__,__LINE__,_("Failed to copy image %s to %s\n"),srcfile,dstfile);
1188 break;
1189 }
1190 }
1191 if (fclose(img_ou)==EOF) {
1192 debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),dstfile,strerror(errno));
1193 exit(EXIT_FAILURE);
1194 }
1195 } else
1196 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"), dstfile, strerror(errno));
1197 if (fclose(img_in)==EOF) {
1198 debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),srcfile,strerror(errno));
1199 exit(EXIT_FAILURE);
1200 }
1201 } else
1202 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"), srcfile, strerror(errno));
1203 }
1204 }
1205 (void) closedir(dirp);
1206
1207 return;
1208 }
1209
1210 /*!
1211 * Check if the proposed file name conforms to the directory structure layed out
1212 * as a file tree. It is used to check if the file name enumerated while scanning
1213 * a directory content may have been created by sarg running with IndexTree set to
1214 * INDEX_TREE_FILE.
1215 */
1216 bool IsTreeFileDirName(const char *Name)
1217 {
1218 char DateFormat;
1219 int i;
1220
1221 // start year (date format u) or start day (date format e)
1222 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1223
1224 if (isdigit(Name[2]) && isdigit(Name[3]))
1225 {
1226 // date format is either u or w
1227 if (Name[4]=='.')
1228 {
1229 // date format is w
1230 if (!isdigit(Name[5]) || !isdigit(Name[6])) return(false);
1231 return(true);//date format w is confirmed
1232 }
1233
1234 // date format is u
1235 Name+=4;
1236
1237 // start month
1238 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1239 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1240 if (i<0) return(false);
1241 Name+=3;
1242
1243 // start day
1244 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1245 Name+=2;
1246
1247 DateFormat='u';
1248 }
1249 else if (isalpha(Name[2]) && isalpha(Name[3]) && isalpha(Name[4]))
1250 {
1251 // date format is e
1252 Name+=2;
1253
1254 // start month
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 // start day
1261 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1262 Name+=4;
1263
1264 DateFormat='e';
1265 }
1266 else
1267 return(false);
1268
1269 if (Name[0]!='-') return(false);
1270 Name++;
1271
1272 if (DateFormat=='u')
1273 {
1274 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1275 Name+=4;
1276
1277 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1278 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1279 if (i<0) return(false);
1280 Name+=3;
1281
1282 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1283 Name+=2;
1284 }
1285 else //DateFormat=='e'
1286 {
1287 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1288 Name+=2;
1289
1290 if (!isalpha(Name[0]) || !isalpha(Name[1]) || !isalpha(Name[2])) return(false);
1291 for (i=11 ; i>=0 && memcmp(mtab1[i],Name,3) ; i--);
1292 if (i<0) return(false);
1293 Name+=3;
1294
1295 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1296 Name+=4;
1297 }
1298 /*
1299 * The directory name may contains additional characters such as a counter if
1300 * a previous report is never overwritten.
1301 */
1302 return(true);
1303 }
1304
1305 /*!
1306 * Check if the proposed file name can be the year part of a report tree build with
1307 * IndexTree set to INDEX_TREE_DATE.
1308 */
1309 bool IsTreeYearFileName(const char *Name)
1310 {
1311 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1312 Name+=4;
1313 if (Name[0]=='-')
1314 {
1315 Name++;
1316 if (!isdigit(Name[0]) || !isdigit(Name[1]) || !isdigit(Name[2]) || !isdigit(Name[3])) return(false);
1317 Name+=4;
1318 }
1319 if (Name[0]) return(false);
1320 return(true);
1321 }
1322
1323 /*!
1324 * Check if the proposed file name can be the month part of a report tree build with
1325 * IndexTree set to INDEX_TREE_DATE.
1326 */
1327 bool IsTreeMonthFileName(const char *Name)
1328 {
1329 int m;
1330
1331 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1332 m=(Name[0]-'0')*10+(Name[1]-'0');
1333 if (m<1 || m>12) return(false);
1334 Name+=2;
1335 if (Name[0]=='-')
1336 {
1337 Name++;
1338 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1339 m=(Name[0]-'0')*10+(Name[1]-'0');
1340 if (m<1 || m>12) return(false);
1341 Name+=2;
1342 }
1343 if (Name[0]) return(false);
1344 return(true);
1345 }
1346
1347 /*!
1348 * Check if the proposed file name can be the day part of a report tree build with
1349 * IndexTree set to INDEX_TREE_DATE.
1350 */
1351 bool IsTreeDayFileName(const char *Name)
1352 {
1353 int d;
1354
1355 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1356 d=(Name[0]-'0')*10+(Name[1]-'0');
1357 if (d<1 || d>31) return(false);
1358 if (Name[2]=='-')
1359 {
1360 Name+=3;
1361 if (!isdigit(Name[0]) || !isdigit(Name[1])) return(false);
1362 d=(Name[0]-'0')*10+(Name[1]-'0');
1363 if (d<1 || d>31) return(false);
1364 }
1365 /*
1366 * The directory name may contains additional characters such as a counter if
1367 * a previous report is never overwritten.
1368 */
1369 return(true);
1370 }
1371
1372 /*!
1373 * Create a directory to generate a report for the specified connection data
1374 * and populate it with the a <tt>sarg-date</tt> file containing the current
1375 * date.
1376 *
1377 * The function also create an <tt>images</tt> directory in \a dir and copy all
1378 * the files from the <tt>SYSCONFDIR/images</tt> into that directory.
1379 *
1380 * \param per1 The date range in the form: YYYYMMMDD-YYYYMMMDD or DDMMMYYYY-DDMMMYYYY depending on the value of
1381 * ::DateFormat.
1382 * \param addr The ip address or host name to which the report is limited. If the string is empty, all the addresses are accepted.
1383 * \param site The destination site to which the report is limited. If the string is empty, all the sites are accepted.
1384 * \param us The user to whom the report is limited. It is an empty string if all the users are accepted.
1385 */
1386 int vrfydir(const struct periodstruct *per1, const char *addr, const char *site, const char *us)
1387 {
1388 FILE *fp_ou;
1389 char wdir[MAXLEN];
1390 int y1, y2;
1391 int m1, m2;
1392 int d1, d2;
1393 int wlen, wlen2;
1394 time_t curtime;
1395 struct tm *loctm;
1396
1397 strcpy(wdir,outdir);
1398 wlen=strlen(wdir);
1399 y1=per1->start.tm_year+1900;
1400 y2=per1->end.tm_year+1900;
1401 m1=per1->start.tm_mon+1;
1402 m2=per1->end.tm_mon+1;
1403 d1=per1->start.tm_mday;
1404 d2=per1->end.tm_mday;
1405 if(IndexTree == INDEX_TREE_DATE) {
1406 wlen+=sprintf(wdir+wlen,"%04d",y1);
1407 if(y1!=y2) wlen+=sprintf(wdir+wlen,"-%04d",y2);
1408 if(access(wdir, R_OK) != 0)
1409 my_mkdir(wdir);
1410
1411 wlen+=sprintf(wdir+wlen,"/%02d",m1);
1412 if(m1 != m2) wlen+=sprintf(wdir+wlen,"-%02d",m2);
1413 if(access(wdir, R_OK) != 0)
1414 my_mkdir(wdir);
1415
1416 wlen+=sprintf(wdir+wlen,"/%02d",d1);
1417 if(d1!=d2) wlen+=sprintf(wdir+wlen,"-%02d",d2);
1418 } else {
1419 if (df == 'u') {
1420 wlen=snprintf(wdir+wlen,sizeof(wdir)-wlen,"%04d%s%02d-%04d%s%02d",y1,
1421 conv_month_name(m1),d1,y2,conv_month_name(m2),d2);
1422 } else if (df == 'e') {
1423 wlen=snprintf(wdir+wlen,sizeof(wdir)-wlen,"%02d%s%04d-%02d%s%04d",d1,
1424 conv_month_name(m1),y1,d2,conv_month_name(m2),y2);
1425 } else if (df == 'w') {
1426 wlen2=strftime(wdir+wlen, sizeof(wdir)-wlen, "%Y.%U", &per1->start);
1427 if (wlen2==0) return(-1);
1428 wlen+=wlen2;
1429 }
1430 }
1431
1432 if(us[0] != '\0') {
1433 struct userinfostruct *uinfo=userinfo_find_from_id(us);
1434 if (uinfo) {
1435 strcat(wdir,"-");
1436 strcat(wdir,uinfo->filename);
1437 }
1438 }
1439 if(addr[0] != '\0') {
1440 strcat(wdir,"-");
1441 strcat(wdir,addr);
1442 }
1443 if(site[0] != '\0') {
1444 strcat(wdir,"-");
1445 strcat(wdir,site);
1446 }
1447
1448 strcpy(outdirname,wdir);
1449
1450 // manufacture a new unique name if configured to keep old reports or overwrite old report if configured to do so
1451 if (!OverwriteReport) {
1452 int num=1;
1453
1454 while (access(wdir,R_OK)==0 || errno==EACCES) //file exist or can't be read
1455 {
1456 sprintf(wdir,"%s.%d",outdirname,num);
1457 num++;
1458 }
1459 if (num>1) {
1460 if(debug)
1461 debuga(__FILE__,__LINE__,_("File %s already exists, moved to %s\n"),outdirname,wdir);
1462 rename(outdirname,wdir);
1463 }
1464 } else {
1465 if(access(outdirname,R_OK) == 0) {
1466 unlinkdir(outdirname,1);
1467 }
1468 }
1469 my_mkdir(outdirname);
1470
1471 // create sarg-date to keep track of the report creation date
1472 if (snprintf(wdir,sizeof(wdir),"%s/sarg-date",outdirname)>=sizeof(wdir)) {
1473 debuga(__FILE__,__LINE__,_("Buffer too small to store "));
1474 debuga_more("%s/sarg-date",outdirname);
1475 exit(EXIT_FAILURE);
1476 }
1477 if ((fp_ou = fopen(wdir, "wt")) == 0) {
1478 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),wdir,strerror(errno));
1479 perror("SARG:");
1480 exit(EXIT_FAILURE);
1481 }
1482 time(&curtime);
1483 //strftime(wdir,sizeof(wdir),"%a %b %d %H:%M:%S %Z %Y",localtime(&curtime));
1484 loctm=localtime(&curtime);
1485 strftime(wdir,sizeof(wdir),"%Y-%m-%d %H:%M:%S",loctm);
1486 if (fprintf(fp_ou,"%s %d\n",wdir,loctm->tm_isdst)<0) {
1487 debuga(__FILE__,__LINE__,_("Failed to write the date in %s\n"),wdir);
1488 perror("SARG:");
1489 exit(EXIT_FAILURE);
1490 }
1491 if (fclose(fp_ou)==EOF) {
1492 debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),wdir,strerror(errno));
1493 exit(EXIT_FAILURE);
1494 }
1495
1496 copy_images();
1497 return(0);
1498 }
1499
1500 /*!
1501 Copy a string without overflowing the buffer. The copied string
1502 is properly terminated by an ASCII zero.
1503
1504 \param dest The destination buffer.
1505 \param src The source buffer.
1506 \param length The size of the destination buffer. The program is aborted
1507 if the length is negative or zero.
1508 */
1509 void safe_strcpy(char *dest,const char *src,int length)
1510 {
1511 if (length<=0) {
1512 debuga(__FILE__,__LINE__,_("Invalid buffer length passed to the function to safely copy a string\n"));
1513 exit(EXIT_FAILURE);
1514 }
1515 strncpy(dest,src,length-1);
1516 dest[length-1]='\0';
1517 }
1518
1519 void strip_latin(char *line)
1520 {
1521 int i,j;
1522 int skip;
1523
1524 j=0;
1525 skip=0;
1526 for (i=0;line[i];i++){
1527 if (skip){
1528 if (line[i]==';') skip=0;
1529 } else {
1530 if (line[i]=='&')
1531 skip=1;
1532 else
1533 line[j++]=line[i];
1534 }
1535 }
1536 line[j]='\0';
1537 return;
1538 }
1539
1540 void zdate(char *ftime,int ftimesize, char DateFormat)
1541 {
1542 time_t t;
1543 struct tm *local;
1544
1545 t = time(NULL);
1546 local = localtime(&t);
1547 if (DateFormat=='u')
1548 strftime(ftime, ftimesize, "%b/%d/%Y %H:%M", local);
1549 else if (DateFormat=='e')
1550 strftime(ftime, ftimesize, "%d/%b/%Y-%H:%M", local);
1551 else if (DateFormat=='w')
1552 strftime(ftime, ftimesize, "%W-%H-%M", local);
1553 return;
1554 }
1555
1556
1557 char *fixtime(long long int elap)
1558 {
1559 long int num = elap / 1000LL;
1560 int hor = 0;
1561 int min = 0;
1562 int sec = 0;
1563 static char buf[20];
1564
1565 hor=num / 3600L;
1566 min=(num % 3600L) / 60L;
1567 sec=num % 60L;
1568
1569 if(hor==0 && min==0 && sec==0)
1570 strcpy(buf,"0");
1571 else
1572 snprintf(buf,sizeof(buf),"%d:%02d:%02d",hor,min,sec);
1573
1574 return buf;
1575 }
1576
1577
1578 void date_from(char *date,int date_size, int *dfrom, int *duntil)
1579 {
1580 int d0=0;
1581 int m0=0;
1582 int y0=0;
1583 int d1=0;
1584 int m1=0;
1585 int y1=0;
1586
1587 if (isdigit(date[0])) {
1588 int next=-1;
1589
1590 if (sscanf(date,"%d/%d/%d%n",&d0,&m0,&y0,&next)!=3 || y0<100 || m0<1 || m0>12 || d0<1 || d0>31 || next<0) {
1591 debuga(__FILE__,__LINE__,_("The date passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1592 exit(EXIT_FAILURE);
1593 }
1594 if (date[next]=='-') {
1595 if (sscanf(date+next+1,"%d/%d/%d",&d1,&m1,&y1)!=3 || y1<100 || m1<1 || m1>12 || d1<1 || d1>31) {
1596 debuga(__FILE__,__LINE__,_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1597 exit(EXIT_FAILURE);
1598 }
1599 } else if (date[next]!='\0') {
1600 debuga(__FILE__,__LINE__,_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1601 exit(EXIT_FAILURE);
1602 } else {
1603 d1=d0;
1604 m1=m0;
1605 y1=y0;
1606 }
1607 } else {
1608 int i;
1609 time_t Today,t1;
1610 struct tm *Date0,Date1;
1611
1612 if (time(&Today)==(time_t)-1) {
1613 debuga(__FILE__,__LINE__,_("Failed to get the current time\n"));
1614 exit(EXIT_FAILURE);
1615 }
1616 if (sscanf(date,"day-%d",&i)==1) {
1617 if (i<0) {
1618 debuga(__FILE__,__LINE__,_("Invalid number of days in -d parameter\n"));
1619 exit(EXIT_FAILURE);
1620 }
1621 Today-=i*24*60*60;
1622 Date0=localtime(&Today);
1623 if (Date0==NULL) {
1624 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
1625 exit(EXIT_FAILURE);
1626 }
1627 y0=y1=Date0->tm_year+1900;
1628 m0=m1=Date0->tm_mon+1;
1629 d0=d1=Date0->tm_mday;
1630 } else if (sscanf(date,"week-%d",&i)==1) {
1631 /*
1632 There is no portable way to find the first day of the week even though the
1633 information is available in the locale. nl_langinfo has the unofficial
1634 parameters _NL_TIME_FIRST_WEEKDAY and _NL_TIME_WEEK_1STDAY but they are
1635 undocumented as is their return value and it is discouraged to use them.
1636 Beside, nl_langinfo isn't available on windows and the first day of the
1637 week isn't available at all on that system.
1638 */
1639 const int FirstWeekDay=1;
1640 time_t WeekBegin;
1641
1642 if (i<0) {
1643 debuga(__FILE__,__LINE__,_("Invalid number of weeks in -d parameter\n"));
1644 exit(EXIT_FAILURE);
1645 }
1646 Date0=localtime(&Today);
1647 if (Date0==NULL) {
1648 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
1649 exit(EXIT_FAILURE);
1650 }
1651 WeekBegin=Today-((Date0->tm_wday-FirstWeekDay+7)%7)*24*60*60;
1652 WeekBegin-=i*7*24*60*60;
1653 Date0=localtime(&WeekBegin);
1654 if (Date0==NULL) {
1655 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
1656 exit(EXIT_FAILURE);
1657 }
1658 y0=Date0->tm_year+1900;
1659 m0=Date0->tm_mon+1;
1660 d0=Date0->tm_mday;
1661 WeekBegin+=6*24*60*60;
1662 Date0=localtime(&WeekBegin);
1663 if (Date0==NULL) {
1664 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
1665 exit(EXIT_FAILURE);
1666 }
1667 y1=Date0->tm_year+1900;
1668 m1=Date0->tm_mon+1;
1669 d1=Date0->tm_mday;
1670 } else if (sscanf(date,"month-%d",&i)==1) {
1671 if (i<0) {
1672 debuga(__FILE__,__LINE__,_("Invalid number of months in -d parameter\n"));
1673 exit(EXIT_FAILURE);
1674 }
1675 Date0=localtime(&Today);
1676 if (Date0==NULL) {
1677 debuga(__FILE__,__LINE__,_("Cannot convert local time: %s\n"),strerror(errno));
1678 exit(EXIT_FAILURE);
1679 }
1680 if (Date0->tm_mon<i%12) {
1681 y0=Date0->tm_year+1900-i/12-1;
1682 m0=(Date0->tm_mon+12-i%12)%12+1;
1683 d0=1;
1684 } else {
1685 y0=Date0->tm_year+1900-i/12;
1686 m0=Date0->tm_mon-i%12+1;
1687 d0=1;
1688 }
1689 memcpy(&Date1,Date0,sizeof(struct tm));
1690 Date1.tm_isdst=-1;
1691 Date1.tm_mday=1;
1692 if (m0<12) {
1693 Date1.tm_mon=m0;
1694 Date1.tm_year=y0-1900;
1695 } else {
1696 Date1.tm_mon=0;
1697 Date1.tm_year=y0-1900+1;
1698 }
1699 t1=mktime(&Date1);
1700 t1-=24*60*60;
1701 Date0=localtime(&t1);
1702 y1=Date0->tm_year+1900;
1703 m1=Date0->tm_mon+1;
1704 d1=Date0->tm_mday;
1705 } else {
1706 debuga(__FILE__,__LINE__,_("Invalid date range passed on command line\n"));
1707 exit(EXIT_FAILURE);
1708 }
1709 }
1710
1711 *dfrom=y0*10000+m0*100+d0;
1712 *duntil=y1*10000+m1*100+d1;
1713 snprintf(date,date_size,"%02d/%02d/%04d-%02d/%02d/%04d",d0,m0,y0,d1,m1,y1);
1714 return;
1715 }
1716
1717
1718 char *strlow(char *string)
1719 {
1720 char *s;
1721
1722 if (string)
1723 {
1724 for (s = string; *s; ++s)
1725 *s = tolower(*s);
1726 }
1727
1728 return string;
1729 }
1730
1731
1732
1733
1734 char *strup(char *string)
1735 {
1736 char *s;
1737
1738 if (string)
1739 {
1740 for (s = string; *s; ++s)
1741 *s = toupper(*s);
1742 }
1743
1744 return string;
1745 }
1746
1747
1748 void removetmp(const char *outdir)
1749 {
1750 FILE *fp_gen;
1751 char filename[256];
1752
1753 if(!RemoveTempFiles)
1754 return;
1755
1756 if(debug) {
1757 debuga(__FILE__,__LINE__,_("Purging temporary file sarg-general\n"));
1758 }
1759 if (snprintf(filename,sizeof(filename),"%s/sarg-general",outdir)>=sizeof(filename)) {
1760 debuga(__FILE__,__LINE__,_("Path too long: "));
1761 debuga_more("%s/sarg-period\n",outdir);
1762 exit(EXIT_FAILURE);
1763 }
1764 if((fp_gen=fopen(filename,"w"))==NULL){
1765 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),filename,strerror(errno));
1766 exit(EXIT_FAILURE);
1767 }
1768 totalger(fp_gen,filename);
1769 if (fclose(fp_gen)==EOF) {
1770 debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),filename,strerror(errno));
1771 exit(EXIT_FAILURE);
1772 }
1773 }
1774
1775 void load_excludecodes(const char *ExcludeCodes)
1776 {
1777 FILE *fp_in;
1778 char data[80];
1779 int i;
1780 int Stored;
1781 long int MemSize;
1782
1783 if(ExcludeCodes[0] == '\0')
1784 return;
1785
1786 if((fp_in=fopen(ExcludeCodes,"r"))==NULL) {
1787 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
1788 exit(EXIT_FAILURE);
1789 }
1790
1791 if (fseek(fp_in, 0, SEEK_END)==-1) {
1792 debuga(__FILE__,__LINE__,_("Failed to move till the end of file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
1793 exit(EXIT_FAILURE);
1794 }
1795 MemSize = ftell(fp_in);
1796 if (MemSize<0) {
1797 debuga(__FILE__,__LINE__,_("Cannot get the size of file \"%s\"\n"),ExcludeCodes);
1798 exit(EXIT_FAILURE);
1799 }
1800 if (fseek(fp_in, 0, SEEK_SET)==-1) {
1801 debuga(__FILE__,__LINE__,_("Failed to rewind file \"%s\": %s\n"),ExcludeCodes,strerror(errno));
1802 exit(EXIT_FAILURE);
1803 }
1804
1805 MemSize+=1;
1806 if((excludecode=(char *) malloc(MemSize))==NULL) {
1807 debuga(__FILE__,__LINE__,_("malloc error (%ld bytes required)\n"),MemSize);
1808 exit(EXIT_FAILURE);
1809 }
1810 memset(excludecode,0,MemSize);
1811
1812 Stored=0;
1813 while(fgets(data,sizeof(data),fp_in)!=NULL) {
1814 if (data[0]=='#') continue;
1815 for (i=strlen(data)-1 ; i>=0 && (unsigned char)data[i]<=' ' ; i--) data[i]='\0';
1816 if (i<0) continue;
1817 if (Stored+i+2>=MemSize) {
1818 debuga(__FILE__,__LINE__,_("Too many codes to exclude in file \"%s\"\n"),ExcludeCodes);
1819 break;
1820 }
1821 strcat(excludecode,data);
1822 strcat(excludecode,";");
1823 Stored+=i+1;
1824 }
1825
1826 if (fclose(fp_in)==EOF) {
1827 debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),ExcludeCodes,strerror(errno));
1828 exit(EXIT_FAILURE);
1829 }
1830 return;
1831 }
1832
1833 void free_excludecodes(void)
1834 {
1835 if (excludecode) {
1836 free(excludecode);
1837 excludecode=NULL;
1838 }
1839 }
1840
1841 int vercode(const char *code)
1842 {
1843 char *cod;
1844 int clen;
1845
1846 if (excludecode && excludecode[0]!='\0') {
1847 clen=strlen(code);
1848 cod=excludecode;
1849 while (cod) {
1850 if (strncmp(code,cod,clen)==0 && cod[clen]==';')
1851 return 1;
1852 cod=strchr(cod,';');
1853 if (cod) cod++;
1854 }
1855 }
1856 return 0;
1857 }
1858
1859 void fixnone(char *str)
1860 {
1861 int i;
1862
1863 for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--);
1864 if(i==3 && strncmp(str,"none",4) == 0)
1865 str[0]='\0';
1866
1867 return;
1868 }
1869
1870 void fixendofline(char *str)
1871 {
1872 int i;
1873
1874 for (i=strlen(str)-1 ; i>=0 && (unsigned char)str[i]<=' ' ; i--) str[i]=0;
1875 }
1876
1877 #ifdef LEGACY_TESTVALIDUSERCHAR
1878 int testvaliduserchar(const char *user)
1879 {
1880 int x=0;
1881 int y=0;
1882
1883 for (y=0; y<strlen(UserInvalidChar); y++) {
1884 for (x=0; x<strlen(user); x++) {
1885 if(user[x] == UserInvalidChar[y])
1886 return 1;
1887 }
1888 }
1889 return 0;
1890 }
1891 #else
1892 int testvaliduserchar(const char *user)
1893 {
1894 char * p_UserInvalidChar = UserInvalidChar ;
1895 const char * p_user ;
1896
1897 while( *p_UserInvalidChar ) {
1898 p_user = user ;
1899 while ( *p_user ) {
1900 if( *p_UserInvalidChar == *p_user )
1901 return 1;
1902 p_user++ ;
1903 }
1904 p_UserInvalidChar++ ;
1905 }
1906 return 0;
1907 }
1908 #endif
1909
1910 int compar( const void *a, const void *b )
1911 {
1912 if( *(int *)a > *(int *)b ) return 1;
1913 if( *(int *)a < *(int *)b ) return -1;
1914 return 0;
1915 }
1916
1917 int getnumlist( char *buf, numlist *list, const int len, const int maxvalue )
1918 {
1919 int i, j, d, flag, r1, r2;
1920 char *pbuf, **bp, *strbufs[ 24 ];
1921
1922 bp = strbufs;
1923 strtok( buf, " \t" );
1924 for( *bp = strtok( NULL, "," ), list->len = 0; *bp; *bp = strtok( NULL, "," ) ) {
1925 if( ++bp >= &strbufs[ 24 ] )
1926 break;
1927 list->len++;
1928 }
1929 if( ! list->len )
1930 return -1;
1931 d = 0;
1932 for( i = 0; i < list->len; i++ ) {
1933 if( strchr( strbufs[ i ], '-' ) != 0 ) {
1934 pbuf = strbufs[ i ];
1935 strtok( pbuf, "-" );
1936 pbuf = strtok( NULL, "\0" );
1937 r1 = atoi( strbufs[ i ] );
1938 if( ( r2 = atoi( pbuf ) ) >= maxvalue || r1 >= r2 )
1939 return -1;
1940 if( i + d + ( r2 - r1 ) + 1 <= len ) {
1941 for( j = r1; j <= r2; j++ )
1942 list->list[ i + d++ ] = j;
1943 d--;
1944 }
1945 }
1946 else
1947 if( ( list->list[ i + d ] = atoi( strbufs[ i ] ) ) >= maxvalue )
1948 return 1;
1949 }
1950 list->len += d;
1951 qsort( list->list, list->len, sizeof( int ), compar );
1952 do {
1953 flag = 0;
1954 for( i = 0; i < list->len - 1; i++ )
1955 if( list->list[ i ] == list->list[ i + 1 ] ) {
1956 for( j = i + 1; j < list->len; j++ )
1957 list->list[ j - 1 ] = list->list[ j ];
1958 list->len--;
1959 flag = 1;
1960 break;
1961 }
1962 } while( flag );
1963 return 0;
1964 }
1965
1966 void show_info(FILE *fp_ou)
1967 {
1968 char ftime[127];
1969
1970 if(!ShowSargInfo) return;
1971 zdate(ftime, sizeof(ftime), df);
1972 fputs("<div class=\"info\">",fp_ou);
1973 fprintf(fp_ou,_("Generated by <a href=\"%s\">%s-%s</a> on %s"),URL,PGM,VERSION,ftime);
1974 fputs("</div>\n",fp_ou);
1975 }
1976
1977 void show_sarg(FILE *fp_ou, int depth)
1978 {
1979 int i;
1980
1981 if(!ShowSargLogo) return;
1982 fputs("<div class=\"logo\"><a href=\"http://sarg.sourceforge.net\"><img src=\"",fp_ou);
1983 for (i=0 ; i<depth ; i++)
1984 fputs("../",fp_ou);
1985 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);
1986 }
1987
1988 void write_logo_image(FILE *fp_ou)
1989 {
1990 if(LogoImage[0]!='\0')
1991 fprintf(fp_ou, "<div class=\"logo\"><img src=\"%s\" width=\"%s\" height=\"%s\" alt=\"Logo\">&nbsp;%s</div>\n",LogoImage,Width,Height,LogoText);
1992 }
1993
1994 void write_html_head(FILE *fp_ou, int depth, const char *page_title,int javascript)
1995 {
1996 int i;
1997
1998 fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n",fp_ou);
1999 fprintf(fp_ou, "<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet);
2000 if (page_title) fprintf(fp_ou,"<title>%s</title>\n",page_title);
2001 css(fp_ou);
2002 if ((javascript & HTML_JS_SORTTABLE)!=0 && SortTableJs[0]) {
2003 fputs("<script type=\"text/javascript\" src=\"",fp_ou);
2004 if (strncmp(SortTableJs,"../",3)==0) {
2005 for (i=0 ; i<depth ; i++) fputs("../",fp_ou);
2006 }
2007 fputs(SortTableJs,fp_ou);
2008 fputs("\"></script>\n",fp_ou);
2009 }
2010 fputs("</head>\n<body>\n",fp_ou);
2011 }
2012
2013 void write_html_header(FILE *fp_ou, int depth, const char *page_title,int javascript)
2014 {
2015 write_html_head(fp_ou,depth,page_title,javascript);
2016 write_logo_image(fp_ou);
2017 show_sarg(fp_ou, depth);
2018 fprintf(fp_ou,"<div class=\"title\"><table cellpadding=\"0\" cellspacing=\"0\">\n<tr><th class=\"title_c\">%s</th></tr>\n",Title);
2019 }
2020
2021 void close_html_header(FILE *fp_ou)
2022 {
2023 fputs("</table></div>\n",fp_ou);
2024 }
2025
2026 void write_html_trailer(FILE *fp_ou)
2027 {
2028 show_info(fp_ou);
2029 fputs("</body>\n</html>\n",fp_ou);
2030 }
2031
2032 void output_html_string(FILE *fp_ou,const char *str,int maxlen)
2033 {
2034 int i=0;
2035
2036 while (*str && (maxlen<=0 || i<maxlen)) {
2037 switch (*str) {
2038 case '&':
2039 fputs("&amp;",fp_ou);
2040 break;
2041 case '<':
2042 fputs("&lt;",fp_ou);
2043 break;
2044 case '>':
2045 fputs("&gt;",fp_ou);
2046 break;
2047 case '"':
2048 fputs("&quot;",fp_ou);
2049 break;
2050 case '\'':
2051 fputs("&#39;",fp_ou);
2052 break;
2053 default:
2054 fputc(*str,fp_ou);
2055 }
2056 str++;
2057 i++;
2058 }
2059 if (maxlen>0 && i>=maxlen)
2060 fputs("&hellip;",fp_ou);
2061 }
2062
2063 void output_html_url(FILE *fp_ou,const char *url)
2064 {
2065 while (*url) {
2066 if (*url=='&')
2067 fputs("&amp;",fp_ou);
2068 else
2069 fputc(*url,fp_ou);
2070 url++;
2071 }
2072 }
2073
2074 /*!
2075 Write a host name inside an A tag of a HTML file. If the host name starts
2076 with a star, it is assumed to be an alias that cannot be put inside a link
2077 so the A tag is not written around the host name.
2078
2079 \param fp_ou The handle of the HTML file.
2080 \param url The host to display in the HTML file.
2081 \param maxlen The maximum number of characters to print into the host name.
2082 */
2083 void output_html_link(FILE *fp_ou,const char *url,int maxlen)
2084 {
2085 if (url[0]==ALIAS_PREFIX) {
2086 // this is an alias, no need for a A tag
2087 output_html_string(fp_ou,url+1,100);
2088 } else {
2089 if (skip_scheme(url)==url)
2090 fputs("<a href=\"http://",fp_ou);//no scheme in the url, assume http:// to make the link clickable
2091 else
2092 fputs("<a href=\"",fp_ou);//the scheme is in the url, no need to add one
2093 output_html_url(fp_ou,url);
2094 fputs("\">",fp_ou);
2095 output_html_string(fp_ou,url,100);
2096 fputs("</a>",fp_ou);
2097 }
2098 }
2099
2100 void url_module(const char *url, char *w2)
2101 {
2102 int x, y;
2103 char w[255];
2104
2105 y=0;
2106 for(x=strlen(url)-1; x>=0; x--) {
2107 if(url[x] == '/' || y>=sizeof(w)-1) break;
2108 w[y++]=url[x];
2109 }
2110 if (x<0) {
2111 w2[0]='\0';
2112 return;
2113 }
2114
2115 x=0;
2116 for(y=y-1; y>=0; y--) {
2117 w2[x++]=w[y];
2118 }
2119 w2[x]='\0';
2120 }
2121
2122 /*!
2123 Mangle an URL to produce a part that can be used as an anchor in
2124 a html <a name=""> tag.
2125
2126 \param url The URL to mangle.
2127 \param anchor The buffer to write the mangled URL.
2128 \param size The size of the buffer.
2129 */
2130 void url_to_anchor(const char *url,char *anchor,int size)
2131 {
2132 int i,j;
2133 bool skip;
2134
2135 // find url end
2136 for (i=0 ; url[i] && url[i]!='/' && url[i]!='?' ; i++);
2137 i--;
2138 if (i<=0) {
2139 anchor[0]='\0';
2140 return;
2141 }
2142
2143 // only keep really safe characters
2144 skip=false;
2145 j=size-1;
2146 anchor[j]='\0';
2147 while (j>0 && i>=0)
2148 {
2149 if(isalnum(url[i]) || url[i]=='-' || url[i]=='_' || url[i]=='.') {
2150 anchor[--j]=url[i];
2151 skip=false;
2152 } else {
2153 if (!skip) anchor[--j]='_';
2154 skip=true;
2155 }
2156 i--;
2157 }
2158 if (j>0)
2159 {
2160 while ( anchor[j])
2161 {
2162 *anchor=anchor[j];
2163 anchor++;
2164 }
2165 *anchor='\0';
2166 }
2167 }
2168
2169 void version(void)
2170 {
2171 printf(_("SARG Version: %s\n"),VERSION);
2172 #if defined(ENABLE_NLS) && defined(HAVE_LOCALE_H)
2173 if (debug) {
2174 printf(_("\nFor the translation to work, a valid message file should be copied to "
2175 "\"%s/<Locale>/LC_MESSAGES/%s.mo\" where <Locale> is derived from the effective locale.\n"),LOCALEDIR,PACKAGE_NAME);
2176 if (CurrentLocale) {
2177 printf(_("Currently effective locale is \"%s\".\n"),CurrentLocale);
2178 } else {
2179 printf(_("Locale is not set in the environment variable.\n"));
2180 }
2181 // TRANSLATORS: You may change this message to tell the reader that the language is correctly supported.
2182 printf(_("If this message is in English, then your language is not supported or not correctly installed.\n"));
2183 }
2184 #endif
2185 if (debug) {
2186 #ifdef HAVE_GLOB_H
2187 printf(_("File globbing compiled in.\n"));
2188 #else
2189 printf(_("File globbing NOT compiled in.\n"));
2190 #endif
2191 }
2192 exit(EXIT_SUCCESS);
2193 }
2194
2195 char *get_param_value(const char *param,char *line)
2196 {
2197 int plen;
2198
2199 while (*line==' ' || *line=='\t') line++;
2200 plen=strlen(param);
2201 if (strncasecmp(line,param,plen)) return(NULL);
2202 if (line[plen]!=' ' && line[plen]!='\t') return(NULL);
2203 line+=plen;
2204 while (*line==' ' || *line=='\t') line++;
2205 return(line);
2206 }
2207
2208 void unlinkdir(const char *dir,bool contentonly)
2209 {
2210 struct stat st;
2211 DIR *dirp;
2212 struct dirent *direntp;
2213 char dname[MAXLEN];
2214 int err;
2215
2216 dirp=opendir(dir);
2217 if (!dirp) return;
2218 while ((direntp = readdir(dirp)) != NULL) {
2219 if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
2220 (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
2221 continue;
2222 if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
2223 debuga(__FILE__,__LINE__,_("Path too long: "));
2224 debuga_more("%s/%s\n",dir,direntp->d_name);
2225 exit(EXIT_FAILURE);
2226 }
2227 #ifdef HAVE_LSTAT
2228 err=lstat(dname,&st);
2229 #else
2230 err=stat(dname,&st);
2231 #endif
2232 if (err) {
2233 debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
2234 exit(EXIT_FAILURE);
2235 }
2236 if (S_ISREG(st.st_mode)) {
2237 if (unlink(dname)) {
2238 debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),dname,strerror(errno));
2239 exit(EXIT_FAILURE);
2240 }
2241 } else if (S_ISDIR(st.st_mode)) {
2242 unlinkdir(dname,0);
2243 } else {
2244 debuga(__FILE__,__LINE__,_("Don't know how to delete \"%s\" (not a regular file nor a directory)\n"),dname);
2245 }
2246 }
2247 closedir(dirp);
2248
2249 if (!contentonly) {
2250 if (rmdir(dir)) {
2251 debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),dir,strerror(errno));
2252 exit(EXIT_FAILURE);
2253 }
2254 }
2255 }
2256
2257 /*!
2258 Delete every file from the temporary directory where sarg is told to store its
2259 temporary files.
2260
2261 As any stray file left over by a previous run would be included in the report, we
2262 must delete every file from the temporary directory before we start processing the logs.
2263
2264 But the temporary directory is given by the user either in the configuration file or
2265 on the command line. We check that the user didn't give a wrong directory by looking
2266 at the files stored in the directory. If a single file is not one of ours, we abort.
2267
2268 \param dir The temporary directory to purge.
2269 */
2270 void emptytmpdir(const char *dir)
2271 {
2272 struct stat st;
2273 DIR *dirp;
2274 struct dirent *direntp;
2275 int dlen;
2276 int elen;
2277 char dname[MAXLEN];
2278 int err;
2279 int i;
2280 static const char *TmpExt[]=
2281 {
2282 ".int_unsort",
2283 ".int_log",
2284 ".day",
2285 "htmlrel.txt",
2286 ".user_unsort",
2287 ".user_log",
2288 ".utmp",
2289 ".ip",
2290 "lastlog1",
2291 "lastlog",
2292 "emailrep"
2293 };
2294
2295 dirp=opendir(dir);
2296 if (!dirp) return;
2297
2298 // make sure the temporary directory contains only our files
2299 while ((direntp = readdir(dirp)) != NULL) {
2300 if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
2301 (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
2302 continue;
2303
2304 // is it one of our files
2305 dlen=strlen(direntp->d_name);
2306 for (i=sizeof(TmpExt)/sizeof(TmpExt[0])-1 ; i>=0 ; i--) {
2307 elen=strlen(TmpExt[i]);
2308 if (dlen>=elen && strcasecmp(direntp->d_name+dlen-elen,TmpExt[i])==0) break;
2309 }
2310 if (i<0) {
2311 debuga(__FILE__,__LINE__,_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2312 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2313 "directory or manually delete the content of \"%s\"\n"),direntp->d_name,dir,dir);
2314 exit(EXIT_FAILURE);
2315 }
2316
2317 if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
2318 debuga(__FILE__,__LINE__,_("Path too long: "));
2319 debuga_more("%s/%s\n",dir,direntp->d_name);
2320 exit(EXIT_FAILURE);
2321 }
2322
2323 #ifdef HAVE_LSTAT
2324 err=lstat(dname,&st);
2325 #else
2326 err=stat(dname,&st);
2327 #endif
2328 if (err) {
2329 debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
2330 exit(EXIT_FAILURE);
2331 }
2332 if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) {
2333 debuga(__FILE__,__LINE__,_("Unknown path type for \"%s\". Check temporary directory\n"),dname);
2334 exit(EXIT_FAILURE);
2335 }
2336 }
2337 rewinddir(dirp);
2338
2339 // now delete our files
2340 while ((direntp = readdir(dirp)) != NULL) {
2341 if (direntp->d_name[0] == '.' && (direntp->d_name[1] == '\0' ||
2342 (direntp->d_name[1] == '.' && direntp->d_name[2] == '\0')))
2343 continue;
2344
2345 // is it one of our files
2346 dlen=strlen(direntp->d_name);
2347 for (i=sizeof(TmpExt)/sizeof(TmpExt[0])-1 ; i>=0 ; i--) {
2348 elen=strlen(TmpExt[i]);
2349 if (dlen>=elen && strcasecmp(direntp->d_name+dlen-elen,TmpExt[i])==0) break;
2350 }
2351 if (i<0) {
2352 debuga(__FILE__,__LINE__,_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2353 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2354 "directory or manually delete the content of \"%s\"\n"),direntp->d_name,dir,dir);
2355 exit(EXIT_FAILURE);
2356 }
2357
2358 if (snprintf(dname,sizeof(dname),"%s/%s",dir,direntp->d_name)>=sizeof(dname)) {
2359 debuga(__FILE__,__LINE__,_("Path too long: "));
2360 debuga_more("%s/%s\n",dir,direntp->d_name);
2361 exit(EXIT_FAILURE);
2362 }
2363 #ifdef HAVE_LSTAT
2364 err=lstat(dname,&st);
2365 #else
2366 err=stat(dname,&st);
2367 #endif
2368 if (err) {
2369 debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),dname,strerror(errno));
2370 exit(EXIT_FAILURE);
2371 }
2372 if (S_ISDIR(st.st_mode)) {
2373 unlinkdir(dname,0);
2374 } else if (S_ISREG(st.st_mode)) {
2375 if (unlink(dname)) {
2376 debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),dname,strerror(errno));
2377 exit(EXIT_FAILURE);
2378 }
2379 } else {
2380 debuga(__FILE__,__LINE__,_("Don't know how to delete \"%s\" (not a regular file)\n"),dname);
2381 }
2382 }
2383 closedir(dirp);
2384 }
2385
2386 /*!
2387 Extract an url, IPv4 or IPv6 from a buffer. The IP addresses may end with a
2388 prefix size.
2389
2390 \param buf The buffer to parse.
2391 \param text A pointer to set to the beginning of the string pattern. No terminating zero is inserted.
2392 The pointer may be NULL.
2393 \param ipv4 A 4 bytes buffer to store the bytes of the IPv4 address.
2394 \param ipv6 A 8 short integers buffer to store the values of the IPv6 address.
2395 \param nbits The number of prefix bits for an IP address.
2396 \param next The content of the line after the extracted address.
2397
2398 \retval 3 The pattern is a IPv6 address.
2399 \retval 2 The pattern is a IPv4 address.
2400 \retval 1 The patter is a string.
2401 \retval 0 Empty pattern.
2402 */
2403 int extract_address_mask(const char *buf,const char **text,unsigned char *ipv4,unsigned short int *ipv6,int *nbits,const char **next)
2404 {
2405 int i;
2406 int j;
2407 int ip_size;
2408 unsigned int value4, value6;
2409 unsigned short int addr[8];
2410 int addr_len;
2411 int nibble6_len;
2412 int mask, max_mask;
2413 int pad_pos;
2414 int pad_len;
2415 bool bracket=false;
2416 bool port=false;
2417 bool port_num=0;
2418
2419 // skip leading spaces and tabs
2420 while (*buf && (*buf==' ' || *buf=='\t')) buf++;
2421
2422 // find out the nature of the pattern
2423 ip_size=0x60 | 0x04;
2424 if (*buf=='[') {
2425 bracket=true;
2426 ip_size=0x60;
2427 buf++;
2428 }
2429 value4=0U;
2430 value6=0U;
2431 addr_len=0;
2432 nibble6_len=0;
2433 pad_pos=-1;
2434 for (i=0 ; (unsigned char)buf[i]>' ' && buf[i]!='/' && buf[i]!='?' && (!bracket || buf[i]!=']') && ip_size ; i++) {
2435 if (ip_size & 0x04) {
2436 if (isdigit(buf[i])) {
2437 if (port) {
2438 port_num=port_num*10+(buf[i]-'0');
2439 if (port_num>65535) ip_size&=~0x04;
2440 } else {
2441 value4=value4*10+(buf[i]-'0');
2442 if (value4>0xFFU) ip_size&=~0x04;
2443 }
2444 } else if (buf[i]=='.' && addr_len<4) {
2445 addr[addr_len++]=(unsigned short)(value4 & 0xFFU);
2446 value4=0U;
2447 } else if (!port && buf[i]==':') {
2448 port=true;
2449 } else {
2450 ip_size&=~0x04;
2451 }
2452 }
2453 if (ip_size & 0x60) {
2454 if (isdigit(buf[i])) {
2455 value6=(value6<<4)+(buf[i]-'0');
2456 nibble6_len++;
2457 if (value6>0xFFFFU) ip_size&=~0x60;
2458 } else if (toupper(buf[i])>='A' && toupper(buf[i])<='F') {
2459 value6=(value6<<4)+(toupper(buf[i])-'A'+10);
2460 nibble6_len++;
2461 if (value6>0xFFFFU) ip_size&=~0x60;
2462 } else if (buf[i]==':' && addr_len<8) {
2463 if (nibble6_len>0) {
2464 addr[addr_len++]=(unsigned short)(value6 & 0xFFFFU);
2465 nibble6_len=0;
2466 }
2467 value6=0U;
2468 if (buf[i+1]==':') {
2469 pad_pos=addr_len;
2470 i++;
2471 }
2472 } else {
2473 ip_size&=~0x60;
2474 }
2475 }
2476 }
2477 if (i==0) return(0);
2478 if (ip_size & 0x04) {
2479 if (addr_len!=3)
2480 ip_size&=~0x04;
2481 else
2482 addr[addr_len++]=(unsigned short)(value4 & 0xFFU);
2483 }
2484 if (ip_size & 0x60) {
2485 if (pad_pos<0 && addr_len!=7) {
2486 ip_size&=~0x60;
2487 } else if (pad_pos>=0 && addr_len>=7)
2488 ip_size&=~0x60;
2489 else if (nibble6_len>0)
2490 addr[addr_len++]=(unsigned short)(value6 & 0xFFFFU);
2491 }
2492 if (!ip_size) {
2493 if (text) {
2494 *text=buf;
2495 if (bracket) (*text)--;
2496 }
2497 while ((unsigned char)buf[i]>' ') i++;
2498 if (next) *next=buf+i;
2499 return(1);
2500 }
2501 max_mask=(ip_size & 0x04) ? 4*8 : 8*16;
2502 if (buf[i]=='/') {
2503 i++;
2504 mask=atoi(buf+i);
2505 while (isdigit(buf[i])) i++;
2506 if (mask<0 || mask>max_mask) mask=max_mask;
2507 } else
2508 mask=max_mask;
2509 if (ip_size & 0x60 && bracket && buf[i]==']') i++;
2510 if (next) *next=buf+i;
2511 if (ip_size & 0x04) {
2512 if (nbits) *nbits=mask;
2513 for (i=0 ; i<addr_len ; i++)
2514 ipv4[i]=(unsigned char)addr[i];
2515 return(2);
2516 }
2517
2518 // IPv6 address
2519 if (nbits) *nbits=mask;
2520 i=0;
2521 j=0;
2522 if (pad_pos>=0) {
2523 while (i<pad_pos)
2524 ipv6[j++]=(unsigned short int)addr[i++];
2525 pad_len=8-addr_len;
2526 while (j<pad_pos+pad_len)
2527 ipv6[j++]=0;
2528 }
2529 while (i<addr_len)
2530 ipv6[j++]=(unsigned short int)addr[i++];
2531 return(3);
2532 }