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