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