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