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