]>
git.ipfire.org Git - thirdparty/sarg.git/blob - util.c
2 * SARG Squid Analysis Report Generator http://sarg.sourceforge.net
6 * please look at http://sarg.sourceforge.net/donations.php
8 * http://sourceforge.net/projects/sarg/forums/forum/363374
9 * ---------------------------------------------------------------------
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.
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.
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.
27 // #define LEGACY_MY_ATOLL
28 // #define LEGACY_TESTVALIDUSERCHAR
30 #include "include/conf.h"
31 #include "include/defs.h"
33 #if defined(__MINGW32__) && defined(HAVE_DIRECT_H)
38 #if defined(HAVE_BACKTRACE)
39 #define USE_GETWORD_BACKTRACE 1
41 #define USE_GETWORD_BACKTRACE 0
44 static char mtab1
[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
46 //! The list of the HTTP codes to exclude from the report.
47 static char *excludecode
=NULL
;
49 //! Directory where the images are stored.
50 char ImageDir
[MAXLEN
]=IMAGEDIR
;
52 extern char *CurrentLocale
;
54 #if USE_GETWORD_BACKTRACE
55 static void getword_backtrace(void)
61 n
=backtrace(buffer
,sizeof(buffer
)/sizeof(buffer
[0]));
63 calls
=backtrace_symbols(buffer
,n
);
65 debuga(__FILE__
,__LINE__
,_("getword backtrace:\n"));
66 for (i
=0 ; i
<n
; i
++) {
67 fprintf(stderr
,"SARG: %d:%s\n",i
+1,calls
[i
]);
72 #endif //USE_GETWORD_BACKTRACE
74 void getword_start(struct getwordstruct
*gwarea
, const char *line
)
76 gwarea
->beginning
=line
;
81 void getword_restart(struct getwordstruct
*gwarea
)
83 if (gwarea
->modified
) {
84 debuga(__FILE__
,__LINE__
,_("Cannot parse again the line as it was modified\n"));
87 gwarea
->current
=gwarea
->beginning
;
90 int getword(char *word
, int limit
, struct getwordstruct
*gwarea
, char stop
)
94 for(x
=0;((gwarea
->current
[x
]) && (gwarea
->current
[x
] != stop
));x
++) {
97 TRANSLATORS: The %s is the name of the function reporting the
100 debuga(__FILE__
,__LINE__
,_("End of word not found in %s after %d bytes.\n"),__func__
,x
);
101 debuga(__FILE__
,__LINE__
,_("Line=\"%s\"\n"),gwarea
->beginning
);
102 debuga(__FILE__
,__LINE__
,_("Record=\"%s\"\n"),gwarea
->current
);
103 debuga(__FILE__
,__LINE__
,_("searching for \'x%x\'\n"),stop
);
104 word
[(limit
>0) ? limit
-1 : 0]='\0';
105 #if USE_GETWORD_BACKTRACE
110 word
[x
] = gwarea
->current
[x
];
114 if (gwarea
->current
[x
]) ++x
;
119 int getword_limit(char *word
, int limit
, struct getwordstruct
*gwarea
, char stop
)
124 for(x
=0; x
<limit
&& gwarea
->current
[x
] && gwarea
->current
[x
] != stop
;x
++) {
125 word
[x
] = gwarea
->current
[x
];
129 while (*gwarea
->current
&& *gwarea
->current
!= stop
) gwarea
->current
++;
130 if (*gwarea
->current
) ++gwarea
->current
;
134 int getword_multisep(char *word
, int limit
, struct getwordstruct
*gwarea
, char stop
)
138 for(x
=0;((gwarea
->current
[x
]) && (gwarea
->current
[x
] != stop
));x
++) {
140 debuga(__FILE__
,__LINE__
,_("End of word not found in %s after %d bytes.\n"),__func__
,x
);
141 debuga(__FILE__
,__LINE__
,_("Line=\"%s\"\n"),gwarea
->beginning
);
142 debuga(__FILE__
,__LINE__
,_("Record=\"%s\"\n"),gwarea
->current
);
143 debuga(__FILE__
,__LINE__
,_("searching for \'x%x\'\n"),stop
);
144 if (limit
>0) word
[limit
-1]='\0';
145 #if USE_GETWORD_BACKTRACE
148 //exit(EXIT_FAILURE);
151 word
[x
] = gwarea
->current
[x
];
155 while (gwarea
->current
[x
] && gwarea
->current
[x
]==stop
) ++x
;
160 int getword_skip(int limit
, struct getwordstruct
*gwarea
, char stop
)
164 for(x
=0;(gwarea
->current
[x
] && (gwarea
->current
[x
] != stop
));x
++) {
166 debuga(__FILE__
,__LINE__
,_("End of word not found in %s after %d bytes.\n"),__func__
,x
);
167 debuga(__FILE__
,__LINE__
,_("Line=\"%s\"\n"),gwarea
->beginning
);
168 debuga(__FILE__
,__LINE__
,_("Record=\"%s\"\n"),gwarea
->current
);
169 debuga(__FILE__
,__LINE__
,_("searching for \'x%x\'\n"),stop
);
170 #if USE_GETWORD_BACKTRACE
177 if (gwarea
->current
[x
]) ++x
;
182 int getword_atoll(long long int *number
, struct getwordstruct
*gwarea
, char stop
)
188 if (gwarea
->current
[0] == '-') {
191 } else if (gwarea
->current
[0] == '+') {
195 for(x
=0;isdigit(gwarea
->current
[x
]);x
++) {
196 digit
=gwarea
->current
[x
]-'0';
197 if (*number
>= (LLONG_MAX
-digit
)/10) {
199 TRANSLATORS: The first %s is the function name (in the source code) where the
200 overflow is detected.
202 debuga(__FILE__
,__LINE__
,_("Integer overflow detected in %s in line %s\n"),__func__
,gwarea
->beginning
);
205 *number
=(*number
* 10) + digit
;
207 if(gwarea
->current
[x
] && gwarea
->current
[x
]!=stop
) {
209 TRANSLATORS: The %s is the function name, in the source code, where the problem occured.
211 debuga(__FILE__
,__LINE__
,_("End of number not found in %s after %d bytes.\n"),__func__
,x
);
212 debuga(__FILE__
,__LINE__
,_("Line=\"%s\"\n"),gwarea
->beginning
);
213 debuga(__FILE__
,__LINE__
,_("Record=\"%s\"\n"),gwarea
->current
);
214 debuga(__FILE__
,__LINE__
,_("searching for \'x%x\'\n"),stop
);
215 #if USE_GETWORD_BACKTRACE
222 if (gwarea
->current
[x
]) ++x
;
227 int getword_atoi(int *number
, struct getwordstruct
*gwarea
, char stop
)
233 if (gwarea
->current
[0] == '-') {
236 } else if (gwarea
->current
[0] == '+') {
240 for(x
=0;isdigit(gwarea
->current
[x
]);x
++) {
241 digit
=gwarea
->current
[x
]-'0';
242 if (*number
> (INT_MAX
-digit
)/10) {
243 debuga(__FILE__
,__LINE__
,_("Integer overflow detected in %s in line %s\n"),__func__
,gwarea
->beginning
);
246 *number
=(*number
* 10) + digit
;
248 if(gwarea
->current
[x
] && gwarea
->current
[x
]!=stop
) {
249 debuga(__FILE__
,__LINE__
,_("End of number not found in %s after %d bytes.\n"),__func__
,x
);
250 debuga(__FILE__
,__LINE__
,_("Line=\"%s\"\n"),gwarea
->beginning
);
251 debuga(__FILE__
,__LINE__
,_("Record=\"%s\"\n"),gwarea
->current
);
252 debuga(__FILE__
,__LINE__
,_("searching for \'x%x\'\n"),stop
);
253 #if USE_GETWORD_BACKTRACE
260 if (gwarea
->current
[x
]) ++x
;
265 int getword_atol(long int *number
, struct getwordstruct
*gwarea
, char stop
)
271 if (gwarea
->current
[0] == '-') {
274 } else if (gwarea
->current
[0] == '+') {
278 for(x
=0;isdigit(gwarea
->current
[x
]);x
++) {
279 digit
=gwarea
->current
[x
]-'0';
280 if (*number
> (LONG_MAX
-digit
)/10) {
281 debuga(__FILE__
,__LINE__
,_("Integer overflow detected in %s in line %s\n"),__func__
,gwarea
->beginning
);
284 *number
=(*number
* 10) + digit
;
286 if(gwarea
->current
[x
] && gwarea
->current
[x
]!=stop
) {
287 debuga(__FILE__
,__LINE__
,_("End of number not found in %s after %d bytes.\n"),__func__
,x
);
288 debuga(__FILE__
,__LINE__
,_("Line=\"%s\"\n"),gwarea
->beginning
);
289 debuga(__FILE__
,__LINE__
,_("Record=\"%s\"\n"),gwarea
->current
);
290 debuga(__FILE__
,__LINE__
,_("searching for \'x%x\'\n"),stop
);
291 #if USE_GETWORD_BACKTRACE
298 if (gwarea
->current
[x
]) ++x
;
303 int getword_atolu(unsigned long int *number
, struct getwordstruct
*gwarea
, char stop
)
308 if (gwarea
->current
[0] == '-') {
309 debuga(__FILE__
,__LINE__
,_("getword_atolu got a negative number.\n"));
310 debuga(__FILE__
,__LINE__
,_("Line=\"%s\"\n"),gwarea
->beginning
);
311 debuga(__FILE__
,__LINE__
,_("Record=\"%s\"\n"),gwarea
->current
);
314 if (gwarea
->current
[0] == '+') {
318 for(x
=0;isdigit(gwarea
->current
[x
]);x
++) {
319 digit
=gwarea
->current
[x
]-'0';
320 if (*number
> (ULONG_MAX
-digit
)/10) {
321 debuga(__FILE__
,__LINE__
,_("Integer overflow detected in %s in line %s\n"),__func__
,gwarea
->beginning
);
324 *number
=(*number
* 10) + digit
;
326 if(gwarea
->current
[x
] && gwarea
->current
[x
]!=stop
) {
327 debuga(__FILE__
,__LINE__
,_("End of number not found in %s after %d bytes.\n"),__func__
,x
);
328 debuga(__FILE__
,__LINE__
,_("Line=\"%s\"\n"),gwarea
->beginning
);
329 debuga(__FILE__
,__LINE__
,_("Record=\"%s\"\n"),gwarea
->current
);
330 debuga(__FILE__
,__LINE__
,_("searching for \'x%x\'\n"),stop
);
331 #if USE_GETWORD_BACKTRACE
337 if (gwarea
->current
[x
]) ++x
;
343 int getword_ptr(char *orig_line
,char **word
, struct getwordstruct
*gwarea
, char stop
)
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.
355 if (orig_line
&& orig_line
!=gwarea
->beginning
) {
356 debuga(__FILE__
,__LINE__
,_("Invalid buffer passed to getword_ptr\n"));
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';
371 #define MAXLLL 30 //!< Maximum number of digits in long long (a guess).
372 long long int my_atoll (const char *nptr
)
374 long long int returnval
=0LL;
375 int max_digits
= MAXLLL
;
377 // Soak up all the white space
378 while (isspace( *nptr
)) {
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
386 while (--max_digits
&& isdigit( *nptr
))
388 returnval
= ( returnval
* 10 ) + ( *nptr
++ - '0' ) ;
394 int is_absolute(const char *path
)
396 if (*path
=='/') return(1);
398 if (isalpha(path
[0]) && path
[1]==':') return(1);
403 int PortableMkDir(const char *path
,int mode
)
405 #if defined(__linux__)
406 int mkerror
=mkdir(path
,mode
);
409 int mkerror
=_mkdir(path
);
414 void my_mkdir(const char *name
)
420 if(!is_absolute(name
)) {
421 debuga(__FILE__
,__LINE__
,_("Invalid path (%s). Please, use absolute paths only.\n"),name
);
426 for (i
=0 ; name
[i
] ; i
++) {
428 debuga(__FILE__
,__LINE__
,_("Path too long: "));
429 debuga_more("%s\n",name
);
432 if (chars
>0 && name
[i
] == '/') {
434 if (access(w0
, R_OK
) != 0) {
435 if (PortableMkDir(w0
,0755)) {
436 debuga(__FILE__
,__LINE__
,_("Cannot create directory \"%s\": %s\n"),w0
,strerror(errno
));
441 if (name
[i
] != '/') chars
++;
445 if (access(name
, R_OK
) != 0) {
446 if (PortableMkDir(name
,0755)) {
447 debuga(__FILE__
,__LINE__
,_("Cannot create directory \"%s\": %s\n"),name
,strerror(errno
));
454 void my_lltoa(unsigned long long int n
, char *s
, int ssize
, int len
)
463 debuga(__FILE__
,__LINE__
,_("The requested number of digits passed to my_lltoa (%d) is bigger than the output buffer size (%d)\n"),len
,ssize
);
468 s
[slen
++] = (n
% 10) + '0';
469 } while ((n
/= 10) > 0 && slen
<ssize
);
472 for (i
= 0, j
= slen
-1; i
<j
; i
++, j
--) {
480 for(j
=slen
; j
>=0; j
--)
487 int month2num(const char *month
)
491 for(m
=0 ; m
<12 && strcmp(mtab1
[m
],month
) != 0; m
++);
495 int builddia(int day
, int month
, int year
)
497 return(year
*10000+month
*100+day
);
503 \param date1 The first date to compare.
504 \param date2 The second date to compare.
506 \retval -1 If date1<date2.
507 \retval 0 If date1==date2.
508 \retval 1 if date1>date2.
510 int compare_date(struct tm
*date1
,struct tm
*date2
)
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);
527 void buildymd(const char *dia
, const char *mes
, const char *ano
, char *wdata
,int wdata_size
)
532 snprintf(wdata
,wdata_size
,"%04d%02d%02d",atoi(ano
),nmes
+1,atoi(dia
));
536 int conv_month(const char *month
)
540 for(x
=0; x
<12 && strncmp(mtab1
[x
],month
,3)!=0; x
++);
545 const char *conv_month_name(int month
)
549 if (month
<1 || month
>12) {
550 snprintf(str
,sizeof(str
),"%03d",month
);
553 return(mtab1
[month
-1]);
557 Write a debug message to stderr. The message is prefixed by "SARG:" to identify its origin.
559 \param msg The printf like message to format.
560 \param ... The arguments to format in the message.
562 void debuga(const char *File
,int Line
,const char *msg
,...)
566 if (debugz
>=LogLevel_Source
) {
567 /* The path is removed because every source file is in the same directory.
568 * There is no point in reporting the full path from the build directory.
570 const char *ptr
=strrchr(File
,'/');
572 /* TRANSLATORS: This is the prefix to stderr messages when the debug level is
573 set to display the source file (%s) and the line number (%d). */
574 fprintf(stderr
,_("SARG(%s:%d): "),ptr
,Line
);
576 /* TRANSLATORS: This is the prefix to stderr messages when the debug level
578 fputs(_("SARG: "),stderr
);
581 vfprintf(stderr
,msg
,ap
);
586 Write a debug message to stderr. The message is supposed
587 to be displayed after a message from debuga().
589 \param msg The printf like message to format.
590 \param ... The arguments to format in the message.
592 void debuga_more(const char *msg
,...)
597 vfprintf(stderr
,msg
,ap
);
602 Write a debug message to stderr. The message is prefixed by "SARG: (info)".
604 \param msg The printf like message to format.
605 \param ... The arguments to format in the message.
607 void debugaz(const char *File
,int Line
,const char *msg
,...)
611 if (debugz
>=LogLevel_Source
) {
612 /* The path is removed because every source file is in the same directory.
613 * There is no point in reporting the full path from the build directory.
615 const char *ptr
=strrchr(File
,'/');
617 /* TRANSLATORS: This is the prefix to information messages when the debug level is
618 set to display the source file (%s) and the line number (%d). */
619 fprintf(stderr
,_("SARG(%s:%d): (info) "),ptr
,Line
);
621 /* TRANSLATORS: This is the prefix to information messages when the debug level
623 fputs(_("SARG: (info) "),stderr
);
626 vfprintf(stderr
,msg
,ap
);
631 char *fixnum(long long int value
, int n
)
633 #define MAXLEN_FIXNUM 256
634 char num
[MAXLEN_FIXNUM
]="";
635 char buf
[MAXLEN_FIXNUM
* 2];
637 static char ret
[MAXLEN_FIXNUM
* 2];
639 register int i
, j
, k
;
641 static char abbrev
[30]="";
643 my_lltoa(value
, num
, sizeof(num
), 0);
645 if(DisplayedValues
==DISPLAY_ABBREV
) {
646 numlen
= strlen(num
);
649 else if (numlen
%3 == 1) {
651 abbrev
[1]=(UseComma
) ? ',' : '.';
656 else if (numlen
%3 == 2) {
659 abbrev
[2]=(UseComma
) ? ',' : '.';
664 else if (numlen
%3 == 0) {
668 abbrev
[3]=(UseComma
) ? ',' : '.';
677 else if (numlen
<= 6)
679 else if (numlen
<= 9)
681 else if (numlen
<= 12)
683 else if (numlen
<= 15)
685 else if (numlen
>= 18)
687 else if (numlen
<= 21)
689 else if (numlen
<= 24)
691 else if (numlen
<= 27)
694 strcat(abbrev
,"???");
699 memset(buf
,0,MAXLEN_FIXNUM
*2);
705 for ( i
= strlen(num
) - 1, j
= 0 ; i
> -1; i
--) {
706 if ( k
== 2 && i
!= 0 ) {
709 pbuf
[j
++] = (UseComma
) ? ',' : '.';
719 for ( i
= strlen(pbuf
) - 1, j
= 0 ; i
> -1; i
--, j
++)
728 char *fixnum2(long long int value
, int n
)
730 #define MAXLEN_FIXNUM2 1024
731 char num
[MAXLEN_FIXNUM2
];
732 char buf
[MAXLEN_FIXNUM2
* 2];
734 static char ret
[MAXLEN_FIXNUM2
* 2];
736 register int i
, j
, k
;
738 my_lltoa(value
, num
, sizeof(num
), 0);
739 memset(buf
,0,MAXLEN_FIXNUM2
*2);
745 for ( i
= strlen(num
) - 1, j
= 0 ; i
> -1; i
--) {
746 if ( k
== 2 && i
!= 0 ) {
749 pbuf
[j
++] = (UseComma
) ? ',' : '.';
759 for ( i
= strlen(pbuf
) - 1, j
= 0 ; i
> -1; i
--, j
++)
768 char *buildtime(long long int elap
)
770 long int num
= elap
/ 1000LL;
777 min
=(num
% 3600L) / 60L;
779 snprintf(buf
,sizeof(buf
),"%02d:%02d:%02d",hor
,min
,sec
);
786 Get the date stored in the <tt>sarg-date</tt> file of a directory with the connection data.
788 \param dirname The directory to look for the connection directory.
789 \param name The name of the directory whose <tt>sarg-date</tt> file must be read.
790 \param data The buffer to store the content of the file. It must be more than 80
794 \retval -1 File not found.
796 int obtdate(const char *dirname
, const char *name
, char *data
)
801 if (snprintf(wdir
,sizeof(wdir
),"%s%s/sarg-date",dirname
,name
)>=sizeof(wdir
)) {
802 debuga(__FILE__
,__LINE__
,_("Buffer too small to store "));
803 debuga_more("%s%s/sarg-date",dirname
,name
);
806 if ((fp_in
= fopen(wdir
, "rt")) == 0) {
807 if (snprintf(wdir
,sizeof(wdir
),"%s%s/date",dirname
,name
)>=sizeof(wdir
)) {
808 debuga(__FILE__
,__LINE__
,_("Buffer too small to store "));
809 debuga_more("%s%s/date",dirname
,name
);
812 if ((fp_in
= fopen(wdir
, "rt")) == 0) {
818 if (!fgets(data
,80,fp_in
)) {
819 debuga(__FILE__
,__LINE__
,_("Failed to read the date in file \"%s\"\n"),wdir
);
822 if (fclose(fp_in
)==EOF
) {
823 debuga(__FILE__
,__LINE__
,_("Read error in \"%s\": %s\n"),wdir
,strerror(errno
));
832 void formatdate(char *date
,int date_size
,int year
,int month
,int day
,int hour
,int minute
,int second
,int dst
)
838 memset(<m
,0,sizeof(ltm
));
839 if (year
>=1900) ltm
.tm_year
=year
-1900;
840 if (month
>=1 && month
<=12) ltm
.tm_mon
=month
-1;
841 if (day
>=1 && day
<=31) ltm
.tm_mday
=day
;
842 if (hour
>=0 && hour
<24) ltm
.tm_hour
=hour
;
843 if (minute
>=0 && minute
<60) ltm
.tm_min
=minute
;
844 if (second
>=0 && second
<60) ltm
.tm_sec
=second
;
846 unixtime
=mktime(<m
); //fill the missing entries
847 fulltm
=localtime(&unixtime
);
848 //strftime(date,date_size,"%a %b %d %H:%M:%S %Z %Y",fulltm);
849 strftime(date
,date_size
,"%c",fulltm
);
853 void computedate(int year
,int month
,int day
,struct tm
*t
)
855 memset(t
,0,sizeof(*t
));
856 t
->tm_year
=year
-1900;
862 int obtuser(const char *dirname
, const char *name
)
869 if (snprintf(wdir
,sizeof(wdir
),"%s%s/sarg-users",dirname
,name
)>=sizeof(wdir
)) {
870 debuga(__FILE__
,__LINE__
,_("Buffer too small to store "));
871 debuga_more("%s%s/sarg-users",dirname
,name
);
874 if((fp_in
=fopen(wdir
,"r"))==NULL
) {
875 if (snprintf(wdir
,sizeof(wdir
),"%s%s/users",dirname
,name
)>=sizeof(wdir
)) {
876 debuga(__FILE__
,__LINE__
,_("Buffer too small to store "));
877 debuga_more("%s%s/users",dirname
,name
);
880 if((fp_in
=fopen(wdir
,"r"))==NULL
) {
885 if (!fgets(tuser
,sizeof(tuser
),fp_in
)) {
886 debuga(__FILE__
,__LINE__
,_("Failed to read the number of users in file \"%s\"\n"),wdir
);
889 if (fclose(fp_in
)==EOF
) {
890 debuga(__FILE__
,__LINE__
,_("Read error in \"%s\": %s\n"),wdir
,strerror(errno
));
899 void obttotal(const char *dirname
, const char *name
, int nuser
, long long int *tbytes
, long long int *media
)
904 char user
[MAX_USER_LEN
];
906 struct getwordstruct gwarea
;
912 if (snprintf(wdir
,sizeof(wdir
),"%s%s/sarg-general",dirname
,name
)>=sizeof(wdir
)) {
913 debuga(__FILE__
,__LINE__
,_("Buffer too small to store "));
914 debuga_more("%s%s/sarg-general",dirname
,name
);
917 if ((fp_in
= FileObject_Open(wdir
)) == NULL
) {
918 if (snprintf(wdir
,sizeof(wdir
),"%s%s/general",dirname
,name
)>=sizeof(wdir
)) {
919 debuga(__FILE__
,__LINE__
,_("Buffer too small to store "));
920 debuga_more("%s%s/general",dirname
,name
);
923 if ((fp_in
= FileObject_Open(wdir
)) == NULL
) {
928 if ((line
=longline_create())==NULL
) {
929 debuga(__FILE__
,__LINE__
,_("Not enough memory to read file \"%s\"\n"),wdir
);
933 while((buf
=longline_read(fp_in
,line
))!=NULL
) {
934 if (strncmp(buf
,"TOTAL\t",6) == 0)
936 else if (strncmp(buf
,"TOTAL ",6) == 0)
940 getword_start(&gwarea
,buf
);
941 if (getword(user
,sizeof(user
),&gwarea
,sep
)<0) {
942 debuga(__FILE__
,__LINE__
,_("Invalid user in file \"%s\"\n"),wdir
);
945 if(strcmp(user
,"TOTAL") != 0)
947 if (getword_skip(MAXLEN
,&gwarea
,sep
)<0) {
948 debuga(__FILE__
,__LINE__
,_("Invalid total number of accesses in file \"%s\"\n"),wdir
);
951 if (getword_atoll(tbytes
,&gwarea
,sep
)<0) {
952 debuga(__FILE__
,__LINE__
,_("Invalid number of bytes in file \"%s\"\n"),wdir
);
957 if (FileObject_Close(fp_in
)) {
958 debuga(__FILE__
,__LINE__
,_("Read error in \"%s\": %s\n"),wdir
,FileObject_GetLastCloseError());
961 longline_destroy(&line
);
966 *media
=*tbytes
/ nuser
;
970 int getperiod_fromsarglog(const char *arqtt
,struct periodstruct
*period
)
973 int day0
, month0
, year0
, hour0
, minute0
;
974 int day1
, month1
, year1
, hour1
, minute1
;
977 memset(period
,0,sizeof(*period
));
980 while((str
=strstr(str
,"sarg-"))!=NULL
) {
982 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
983 day0
=(str
[0]-'0')*10+(str
[1]-'0');
984 if (day0
<1 || day0
>31) continue;
986 month0
=(str
[0]-'0')*10+(str
[1]-'0')-1;
987 if (month0
<0 || month0
>11) continue;
990 for (i
=0 ; isdigit(str
[i
]) && i
<4 ; i
++) year0
=year0
*10+(str
[i
]-'0');
993 if (str
[0]!='_') continue;
996 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
997 hour0
=(str
[0]-'0')*10+(str
[1]-'0');
999 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
1000 minute0
=(str
[0]-'0')*10+(str
[1]-'0');
1003 if (*str
!= '-') continue;
1006 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
1007 day1
=(str
[0]-'0')*10+(str
[1]-'0');
1008 if (day1
<1 || day1
>31) continue;
1010 month1
=(str
[0]-'0')*10+(str
[1]-'0')-1;
1011 if (month1
<0 || month1
>11) continue;
1014 for (i
=0 ; isdigit(str
[i
]) && i
<4 ; i
++) year1
=year1
*10+(str
[i
]-'0');
1018 if (str
[0]!='_') continue;
1021 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
1022 hour1
=(str
[0]-'0')*10+(str
[1]-'0');
1024 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
1025 minute1
=(str
[0]-'0')*10+(str
[1]-'0');
1028 period
->start
.tm_mday
=day0
;
1029 period
->start
.tm_mon
=month0
;
1030 period
->start
.tm_year
=year0
-1900;
1031 period
->start
.tm_hour
=hour0
;
1032 period
->start
.tm_min
=minute0
;
1033 period
->end
.tm_mday
=day1
;
1034 period
->end
.tm_mon
=month1
;
1035 period
->end
.tm_year
=year1
-1900;
1036 period
->end
.tm_hour
=hour1
;
1037 period
->end
.tm_min
=minute1
;
1043 void getperiod_fromrange(struct periodstruct
*period
,int dfrom
,int duntil
)
1045 memset(&period
->start
,0,sizeof(period
->start
));
1046 period
->start
.tm_mday
=dfrom
%100;
1047 period
->start
.tm_mon
=(dfrom
/100)%100-1;
1048 period
->start
.tm_year
=(dfrom
/10000)-1900;
1050 memset(&period
->end
,0,sizeof(period
->end
));
1051 period
->end
.tm_mday
=duntil
%100;
1052 period
->end
.tm_mon
=(duntil
/100)%100-1;
1053 period
->end
.tm_year
=(duntil
/10000)-1900;
1057 Update the \a main period to encompass the period in \a candidate.
1059 void getperiod_merge(struct periodstruct
*main
,struct periodstruct
*candidate
)
1064 mdate
=(main
->start
.tm_year
)*10000+(main
->start
.tm_mon
)*100+main
->start
.tm_mday
;
1065 cdate
=(candidate
->start
.tm_year
)*10000+(candidate
->start
.tm_mon
)*100+candidate
->start
.tm_mday
;
1066 if (cdate
<mdate
) memcpy(&main
->start
,&candidate
->start
,sizeof(struct tm
));
1068 mdate
=(main
->end
.tm_year
)*10000+(main
->end
.tm_mon
)*100+main
->end
.tm_mday
;
1069 cdate
=(candidate
->end
.tm_year
)*10000+(candidate
->end
.tm_mon
)*100+candidate
->end
.tm_mday
;
1070 if (cdate
>mdate
) memcpy(&main
->end
,&candidate
->end
,sizeof(struct tm
));
1073 int getperiod_buildtext(struct periodstruct
*period
)
1077 char text1
[40], text2
[40];
1080 i
=strftime(text1
, sizeof(text1
), "%Y %b %d", &period
->start
);
1081 } else if(df
=='e') {
1082 i
=strftime(text1
, sizeof(text1
), "%d %b %Y", &period
->start
);
1083 } else /*if (df=='w')*/ {
1084 IndexTree
=INDEX_TREE_FILE
;
1085 i
=strftime(text1
, sizeof(text1
), "%Y.%U", &period
->start
);
1087 if (i
== 0) return(-1);
1089 range
=(period
->start
.tm_year
!=period
->end
.tm_year
||
1090 period
->start
.tm_mon
!=period
->end
.tm_mon
||
1091 period
->start
.tm_mday
!=period
->end
.tm_mday
);
1094 i
=strftime(text2
, sizeof(text2
)-i
, "%Y %b %d", &period
->end
);
1095 } else if (df
=='e') {
1096 i
=strftime(text2
, sizeof(text2
)-i
, "%d %b %Y", &period
->end
);
1098 i
=strftime(text2
, sizeof(text2
)-i
, "%Y.%U", &period
->end
);
1100 if (i
== 0) return(-1);
1104 snprintf(period
->text
,sizeof(period
->text
),"%s-%s",text1
,text2
);
1105 snprintf(period
->html
,sizeof(period
->html
),"%s—%s",text1
,text2
);
1107 safe_strcpy(period
->text
,text1
,sizeof(period
->text
));
1108 safe_strcpy(period
->html
,text1
,sizeof(period
->html
));
1113 static void copy_images(void)
1115 FILE *img_in
, *img_ou
;
1117 char srcfile
[MAXLEN
];
1118 char dstfile
[MAXLEN
];
1120 struct dirent
*direntp
;
1121 char buffer
[MAXLEN
];
1125 if (snprintf(images
,sizeof(images
),"%simages",outdir
)>=sizeof(images
)) {
1126 debuga(__FILE__
,__LINE__
,_("Cannot copy images to target directory %simages\n"),outdir
);
1129 if (access(images
,R_OK
)!=0) {
1130 if (PortableMkDir(images
,0755)) {
1131 debuga(__FILE__
,__LINE__
,_("Cannot create directory \"%s\": %s\n"),images
,strerror(errno
));
1136 dirp
= opendir(ImageDir
);
1138 debuga(__FILE__
,__LINE__
,_("Cannot open directory \"%s\": %s\n"),ImageDir
,strerror(errno
));
1141 while ((direntp
= readdir( dirp
)) != NULL
){
1142 if(direntp
->d_name
[0]=='.')
1144 if (snprintf(srcfile
,sizeof(srcfile
),"%s/%s",ImageDir
,direntp
->d_name
)>=sizeof(srcfile
)) {
1145 debuga(__FILE__
,__LINE__
,_("Buffer too small to store "));
1146 debuga_more("%s/%s",ImageDir
,direntp
->d_name
);
1149 if (stat(srcfile
,&info
)) {
1150 debuga(__FILE__
,__LINE__
,_("Cannot stat \"%s\": %s\n"),srcfile
,strerror(errno
));
1153 if (S_ISREG(info
.st_mode
)) {
1154 if (snprintf(dstfile
,sizeof(dstfile
),"%s/%s",images
,direntp
->d_name
)>=sizeof(dstfile
)) {
1155 debuga(__FILE__
,__LINE__
,_("Buffer too small to store "));
1156 debuga_more("%s/%s",images
,direntp
->d_name
);
1159 img_in
= fopen(srcfile
, "rb");
1161 img_ou
= fopen(dstfile
, "wb");
1163 while ((nread
= fread(buffer
,1,sizeof(buffer
),img_in
))>0) {
1164 if (fwrite(buffer
,1,nread
,img_ou
)!=nread
) {
1165 debuga(__FILE__
,__LINE__
,_("Failed to copy image %s to %s\n"),srcfile
,dstfile
);
1169 if (fclose(img_ou
)==EOF
) {
1170 debuga(__FILE__
,__LINE__
,_("Write error in \"%s\": %s\n"),dstfile
,strerror(errno
));
1174 debuga(__FILE__
,__LINE__
,_("Cannot open file \"%s\": %s\n"), dstfile
, strerror(errno
));
1175 if (fclose(img_in
)==EOF
) {
1176 debuga(__FILE__
,__LINE__
,_("Read error in \"%s\": %s\n"),srcfile
,strerror(errno
));
1180 debuga(__FILE__
,__LINE__
,_("Cannot open file \"%s\": %s\n"), srcfile
, strerror(errno
));
1183 (void) closedir(dirp
);
1189 * Check if the proposed file name conforms to the directory structure layed out
1190 * as a file tree. It is used to check if the file name enumerated while scanning
1191 * a directory content may have been created by sarg running with IndexTree set to
1194 bool IsTreeFileDirName(const char *Name
)
1199 // start year (date format u) or start day (date format e)
1200 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1202 if (isdigit(Name
[2]) && isdigit(Name
[3]))
1204 // date format is either u or w
1208 if (!isdigit(Name
[5]) || !isdigit(Name
[6])) return(false);
1209 return(true);//date format w is confirmed
1216 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1217 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1218 if (i
<0) return(false);
1222 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1227 else if (isalpha(Name
[2]) && isalpha(Name
[3]) && isalpha(Name
[4]))
1233 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1234 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1235 if (i
<0) return(false);
1239 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1247 if (Name
[0]!='-') return(false);
1250 if (DateFormat
=='u')
1252 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1255 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1256 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1257 if (i
<0) return(false);
1260 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1263 else //DateFormat=='e'
1265 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1268 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1269 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1270 if (i
<0) return(false);
1273 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1277 * The directory name may contains additional characters such as a counter if
1278 * a previous report is never overwritten.
1284 * Check if the proposed file name can be the year part of a report tree build with
1285 * IndexTree set to INDEX_TREE_DATE.
1287 bool IsTreeYearFileName(const char *Name
)
1289 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1294 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1297 if (Name
[0]) return(false);
1302 * Check if the proposed file name can be the month part of a report tree build with
1303 * IndexTree set to INDEX_TREE_DATE.
1305 bool IsTreeMonthFileName(const char *Name
)
1309 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1310 m
=(Name
[0]-'0')*10+(Name
[1]-'0');
1311 if (m
<1 || m
>12) return(false);
1316 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1317 m
=(Name
[0]-'0')*10+(Name
[1]-'0');
1318 if (m
<1 || m
>12) return(false);
1321 if (Name
[0]) return(false);
1326 * Check if the proposed file name can be the day part of a report tree build with
1327 * IndexTree set to INDEX_TREE_DATE.
1329 bool IsTreeDayFileName(const char *Name
)
1333 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1334 d
=(Name
[0]-'0')*10+(Name
[1]-'0');
1335 if (d
<1 || d
>31) return(false);
1339 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1340 d
=(Name
[0]-'0')*10+(Name
[1]-'0');
1341 if (d
<1 || d
>31) return(false);
1344 * The directory name may contains additional characters such as a counter if
1345 * a previous report is never overwritten.
1351 * Create a directory to generate a report for the specified connection data
1352 * and populate it with the a <tt>sarg-date</tt> file containing the current
1355 * The function also create an <tt>images</tt> directory in \a dir and copy all
1356 * the files from the <tt>SYSCONFDIR/images</tt> into that directory.
1358 * \param per1 The date range in the form: YYYYMMMDD-YYYYMMMDD or DDMMMYYYY-DDMMMYYYY depending on the value of
1360 * \param addr The ip address or host name to which the report is limited. If the string is empty, all the addresses are accepted.
1361 * \param site The destination site to which the report is limited. If the string is empty, all the sites are accepted.
1362 * \param us The user to whom the report is limited. It is an empty string if all the users are accepted.
1364 int vrfydir(const struct periodstruct
*per1
, const char *addr
, const char *site
, const char *us
)
1375 strcpy(wdir
,outdir
);
1377 y1
=per1
->start
.tm_year
+1900;
1378 y2
=per1
->end
.tm_year
+1900;
1379 m1
=per1
->start
.tm_mon
+1;
1380 m2
=per1
->end
.tm_mon
+1;
1381 d1
=per1
->start
.tm_mday
;
1382 d2
=per1
->end
.tm_mday
;
1383 if(IndexTree
== INDEX_TREE_DATE
) {
1384 wlen
+=sprintf(wdir
+wlen
,"%04d",y1
);
1385 if(y1
!=y2
) wlen
+=sprintf(wdir
+wlen
,"-%04d",y2
);
1386 if(access(wdir
, R_OK
) != 0)
1389 wlen
+=sprintf(wdir
+wlen
,"/%02d",m1
);
1390 if(m1
!= m2
) wlen
+=sprintf(wdir
+wlen
,"-%02d",m2
);
1391 if(access(wdir
, R_OK
) != 0)
1394 wlen
+=sprintf(wdir
+wlen
,"/%02d",d1
);
1395 if(d1
!=d2
) wlen
+=sprintf(wdir
+wlen
,"-%02d",d2
);
1398 wlen
=snprintf(wdir
+wlen
,sizeof(wdir
)-wlen
,"%04d%s%02d-%04d%s%02d",y1
,
1399 conv_month_name(m1
),d1
,y2
,conv_month_name(m2
),d2
);
1400 } else if (df
== 'e') {
1401 wlen
=snprintf(wdir
+wlen
,sizeof(wdir
)-wlen
,"%02d%s%04d-%02d%s%04d",d1
,
1402 conv_month_name(m1
),y1
,d2
,conv_month_name(m2
),y2
);
1403 } else if (df
== 'w') {
1404 wlen2
=strftime(wdir
+wlen
, sizeof(wdir
)-wlen
, "%Y.%U", &per1
->start
);
1405 if (wlen2
==0) return(-1);
1411 struct userinfostruct
*uinfo
=userinfo_find_from_id(us
);
1414 strcat(wdir
,uinfo
->filename
);
1417 if(addr
[0] != '\0') {
1421 if(site
[0] != '\0') {
1426 strcpy(outdirname
,wdir
);
1428 // manufacture a new unique name if configured to keep old reports or overwrite old report if configured to do so
1429 if (!OverwriteReport
) {
1432 while (access(wdir
,R_OK
)==0 || errno
==EACCES
) //file exist or can't be read
1434 sprintf(wdir
,"%s.%d",outdirname
,num
);
1439 debuga(__FILE__
,__LINE__
,_("File %s already exists, moved to %s\n"),outdirname
,wdir
);
1440 rename(outdirname
,wdir
);
1443 if(access(outdirname
,R_OK
) == 0) {
1444 unlinkdir(outdirname
,1);
1447 my_mkdir(outdirname
);
1449 // create sarg-date to keep track of the report creation date
1450 if (snprintf(wdir
,sizeof(wdir
),"%s/sarg-date",outdirname
)>=sizeof(wdir
)) {
1451 debuga(__FILE__
,__LINE__
,_("Buffer too small to store "));
1452 debuga_more("%s/sarg-date",outdirname
);
1455 if ((fp_ou
= fopen(wdir
, "wt")) == 0) {
1456 debuga(__FILE__
,__LINE__
,_("Cannot open file \"%s\": %s\n"),wdir
,strerror(errno
));
1461 //strftime(wdir,sizeof(wdir),"%a %b %d %H:%M:%S %Z %Y",localtime(&curtime));
1462 loctm
=localtime(&curtime
);
1463 strftime(wdir
,sizeof(wdir
),"%Y-%m-%d %H:%M:%S",loctm
);
1464 if (fprintf(fp_ou
,"%s %d\n",wdir
,loctm
->tm_isdst
)<0) {
1465 debuga(__FILE__
,__LINE__
,_("Failed to write the date in %s\n"),wdir
);
1469 if (fclose(fp_ou
)==EOF
) {
1470 debuga(__FILE__
,__LINE__
,_("Write error in \"%s\": %s\n"),wdir
,strerror(errno
));
1479 Copy a string without overflowing the buffer. The copied string
1480 is properly terminated by an ASCII zero.
1482 \param dest The destination buffer.
1483 \param src The source buffer.
1484 \param length The size of the destination buffer. The program is aborted
1485 if the length is negative or zero.
1487 void safe_strcpy(char *dest
,const char *src
,int length
)
1490 debuga(__FILE__
,__LINE__
,_("Invalid buffer length passed to the function to safely copy a string\n"));
1493 strncpy(dest
,src
,length
-1);
1494 dest
[length
-1]='\0';
1497 void strip_latin(char *line
)
1504 for (i
=0;line
[i
];i
++){
1506 if (line
[i
]==';') skip
=0;
1518 void zdate(char *ftime
,int ftimesize
, char DateFormat
)
1524 local
= localtime(&t
);
1525 if (DateFormat
=='u')
1526 strftime(ftime
, ftimesize
, "%b/%d/%Y %H:%M", local
);
1527 else if (DateFormat
=='e')
1528 strftime(ftime
, ftimesize
, "%d/%b/%Y-%H:%M", local
);
1529 else if (DateFormat
=='w')
1530 strftime(ftime
, ftimesize
, "%W-%H-%M", local
);
1535 char *fixtime(long long int elap
)
1537 long int num
= elap
/ 1000LL;
1541 static char buf
[20];
1544 min
=(num
% 3600L) / 60L;
1547 if(hor
==0 && min
==0 && sec
==0)
1550 snprintf(buf
,sizeof(buf
),"%d:%02d:%02d",hor
,min
,sec
);
1556 void date_from(char *date
,int date_size
, int *dfrom
, int *duntil
)
1565 if (isdigit(date
[0])) {
1568 if (sscanf(date
,"%d/%d/%d%n",&d0
,&m0
,&y0
,&next
)!=3 || y0
<100 || m0
<1 || m0
>12 || d0
<1 || d0
>31 || next
<0) {
1569 debuga(__FILE__
,__LINE__
,_("The date passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1572 if (date
[next
]=='-') {
1573 if (sscanf(date
+next
+1,"%d/%d/%d",&d1
,&m1
,&y1
)!=3 || y1
<100 || m1
<1 || m1
>12 || d1
<1 || d1
>31) {
1574 debuga(__FILE__
,__LINE__
,_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1577 } else if (date
[next
]!='\0') {
1578 debuga(__FILE__
,__LINE__
,_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1588 struct tm
*Date0
,Date1
;
1590 if (time(&Today
)==(time_t)-1) {
1591 debuga(__FILE__
,__LINE__
,_("Failed to get the current time\n"));
1594 if (sscanf(date
,"day-%d",&i
)==1) {
1596 debuga(__FILE__
,__LINE__
,_("Invalid number of days in -d parameter\n"));
1600 Date0
=localtime(&Today
);
1602 debuga(__FILE__
,__LINE__
,_("Cannot convert local time: %s\n"),strerror(errno
));
1605 y0
=y1
=Date0
->tm_year
+1900;
1606 m0
=m1
=Date0
->tm_mon
+1;
1607 d0
=d1
=Date0
->tm_mday
;
1608 } else if (sscanf(date
,"week-%d",&i
)==1) {
1610 There is no portable way to find the first day of the week even though the
1611 information is available in the locale. nl_langinfo has the unofficial
1612 parameters _NL_TIME_FIRST_WEEKDAY and _NL_TIME_WEEK_1STDAY but they are
1613 undocumented as is their return value and it is discouraged to use them.
1614 Beside, nl_langinfo isn't available on windows and the first day of the
1615 week isn't available at all on that system.
1617 const int FirstWeekDay
=1;
1621 debuga(__FILE__
,__LINE__
,_("Invalid number of weeks in -d parameter\n"));
1624 Date0
=localtime(&Today
);
1626 debuga(__FILE__
,__LINE__
,_("Cannot convert local time: %s\n"),strerror(errno
));
1629 WeekBegin
=Today
-((Date0
->tm_wday
-FirstWeekDay
+7)%7)*24*60*60;
1630 WeekBegin
-=i
*7*24*60*60;
1631 Date0
=localtime(&WeekBegin
);
1633 debuga(__FILE__
,__LINE__
,_("Cannot convert local time: %s\n"),strerror(errno
));
1636 y0
=Date0
->tm_year
+1900;
1639 WeekBegin
+=6*24*60*60;
1640 Date0
=localtime(&WeekBegin
);
1642 debuga(__FILE__
,__LINE__
,_("Cannot convert local time: %s\n"),strerror(errno
));
1645 y1
=Date0
->tm_year
+1900;
1648 } else if (sscanf(date
,"month-%d",&i
)==1) {
1650 debuga(__FILE__
,__LINE__
,_("Invalid number of months in -d parameter\n"));
1653 Date0
=localtime(&Today
);
1655 debuga(__FILE__
,__LINE__
,_("Cannot convert local time: %s\n"),strerror(errno
));
1658 if (Date0
->tm_mon
<i
%12) {
1659 y0
=Date0
->tm_year
+1900-i
/12-1;
1660 m0
=(Date0
->tm_mon
+12-i
%12)%12+1;
1663 y0
=Date0
->tm_year
+1900-i
/12;
1664 m0
=Date0
->tm_mon
-i
%12+1;
1667 memcpy(&Date1
,Date0
,sizeof(struct tm
));
1672 Date1
.tm_year
=y0
-1900;
1675 Date1
.tm_year
=y0
-1900+1;
1679 Date0
=localtime(&t1
);
1680 y1
=Date0
->tm_year
+1900;
1684 debuga(__FILE__
,__LINE__
,_("Invalid date range passed on command line\n"));
1689 *dfrom
=y0
*10000+m0
*100+d0
;
1690 *duntil
=y1
*10000+m1
*100+d1
;
1691 snprintf(date
,date_size
,"%02d/%02d/%04d-%02d/%02d/%04d",d0
,m0
,y0
,d1
,m1
,y1
);
1696 char *strlow(char *string
)
1702 for (s
= string
; *s
; ++s
)
1712 char *strup(char *string
)
1718 for (s
= string
; *s
; ++s
)
1726 void removetmp(const char *outdir
)
1731 if(!RemoveTempFiles
)
1735 debuga(__FILE__
,__LINE__
,_("Purging temporary file sarg-general\n"));
1737 if (snprintf(filename
,sizeof(filename
),"%s/sarg-general",outdir
)>=sizeof(filename
)) {
1738 debuga(__FILE__
,__LINE__
,_("Path too long: "));
1739 debuga_more("%s/sarg-period\n",outdir
);
1742 if((fp_gen
=fopen(filename
,"w"))==NULL
){
1743 debuga(__FILE__
,__LINE__
,_("Cannot open file \"%s\": %s\n"),filename
,strerror(errno
));
1746 totalger(fp_gen
,filename
);
1747 if (fclose(fp_gen
)==EOF
) {
1748 debuga(__FILE__
,__LINE__
,_("Write error in \"%s\": %s\n"),filename
,strerror(errno
));
1753 void load_excludecodes(const char *ExcludeCodes
)
1761 if(ExcludeCodes
[0] == '\0')
1764 if((fp_in
=fopen(ExcludeCodes
,"r"))==NULL
) {
1765 debuga(__FILE__
,__LINE__
,_("Cannot open file \"%s\": %s\n"),ExcludeCodes
,strerror(errno
));
1769 if (fseek(fp_in
, 0, SEEK_END
)==-1) {
1770 debuga(__FILE__
,__LINE__
,_("Failed to move till the end of file \"%s\": %s\n"),ExcludeCodes
,strerror(errno
));
1773 MemSize
= ftell(fp_in
);
1775 debuga(__FILE__
,__LINE__
,_("Cannot get the size of file \"%s\"\n"),ExcludeCodes
);
1778 if (fseek(fp_in
, 0, SEEK_SET
)==-1) {
1779 debuga(__FILE__
,__LINE__
,_("Failed to rewind file \"%s\": %s\n"),ExcludeCodes
,strerror(errno
));
1784 if((excludecode
=(char *) malloc(MemSize
))==NULL
) {
1785 debuga(__FILE__
,__LINE__
,_("malloc error (%ld bytes required)\n"),MemSize
);
1788 memset(excludecode
,0,MemSize
);
1791 while(fgets(data
,sizeof(data
),fp_in
)!=NULL
) {
1792 if (data
[0]=='#') continue;
1793 for (i
=strlen(data
)-1 ; i
>=0 && (unsigned char)data
[i
]<=' ' ; i
--) data
[i
]='\0';
1795 if (Stored
+i
+2>=MemSize
) {
1796 debuga(__FILE__
,__LINE__
,_("Too many codes to exclude in file \"%s\"\n"),ExcludeCodes
);
1799 strcat(excludecode
,data
);
1800 strcat(excludecode
,";");
1804 if (fclose(fp_in
)==EOF
) {
1805 debuga(__FILE__
,__LINE__
,_("Read error in \"%s\": %s\n"),ExcludeCodes
,strerror(errno
));
1811 void free_excludecodes(void)
1819 int vercode(const char *code
)
1824 if (excludecode
&& excludecode
[0]!='\0') {
1828 if (strncmp(code
,cod
,clen
)==0 && cod
[clen
]==';')
1830 cod
=strchr(cod
,';');
1837 void fixnone(char *str
)
1841 for (i
=strlen(str
)-1 ; i
>=0 && (unsigned char)str
[i
]<=' ' ; i
--);
1842 if(i
==3 && strncmp(str
,"none",4) == 0)
1848 void fixendofline(char *str
)
1852 for (i
=strlen(str
)-1 ; i
>=0 && (unsigned char)str
[i
]<=' ' ; i
--) str
[i
]=0;
1855 #ifdef LEGACY_TESTVALIDUSERCHAR
1856 int testvaliduserchar(const char *user
)
1861 for (y
=0; y
<strlen(UserInvalidChar
); y
++) {
1862 for (x
=0; x
<strlen(user
); x
++) {
1863 if(user
[x
] == UserInvalidChar
[y
])
1870 int testvaliduserchar(const char *user
)
1872 char * p_UserInvalidChar
= UserInvalidChar
;
1873 const char * p_user
;
1875 while( *p_UserInvalidChar
) {
1878 if( *p_UserInvalidChar
== *p_user
)
1882 p_UserInvalidChar
++ ;
1888 int compar( const void *a
, const void *b
)
1890 if( *(int *)a
> *(int *)b
) return 1;
1891 if( *(int *)a
< *(int *)b
) return -1;
1895 int getnumlist( char *buf
, numlist
*list
, const int len
, const int maxvalue
)
1897 int i
, j
, d
, flag
, r1
, r2
;
1898 char *pbuf
, **bp
, *strbufs
[ 24 ];
1901 strtok( buf
, " \t" );
1902 for( *bp
= strtok( NULL
, "," ), list
->len
= 0; *bp
; *bp
= strtok( NULL
, "," ) ) {
1903 if( ++bp
>= &strbufs
[ 24 ] )
1910 for( i
= 0; i
< list
->len
; i
++ ) {
1911 if( strchr( strbufs
[ i
], '-' ) != 0 ) {
1912 pbuf
= strbufs
[ i
];
1913 strtok( pbuf
, "-" );
1914 pbuf
= strtok( NULL
, "\0" );
1915 r1
= atoi( strbufs
[ i
] );
1916 if( ( r2
= atoi( pbuf
) ) >= maxvalue
|| r1
>= r2
)
1918 if( i
+ d
+ ( r2
- r1
) + 1 <= len
) {
1919 for( j
= r1
; j
<= r2
; j
++ )
1920 list
->list
[ i
+ d
++ ] = j
;
1925 if( ( list
->list
[ i
+ d
] = atoi( strbufs
[ i
] ) ) >= maxvalue
)
1929 qsort( list
->list
, list
->len
, sizeof( int ), compar
);
1932 for( i
= 0; i
< list
->len
- 1; i
++ )
1933 if( list
->list
[ i
] == list
->list
[ i
+ 1 ] ) {
1934 for( j
= i
+ 1; j
< list
->len
; j
++ )
1935 list
->list
[ j
- 1 ] = list
->list
[ j
];
1944 void show_info(FILE *fp_ou
)
1948 if(!ShowSargInfo
) return;
1949 zdate(ftime
, sizeof(ftime
), df
);
1950 fputs("<div class=\"info\">",fp_ou
);
1951 fprintf(fp_ou
,_("Generated by <a href=\"%s\">%s-%s</a> on %s"),URL
,PGM
,VERSION
,ftime
);
1952 fputs("</div>\n",fp_ou
);
1955 void show_sarg(FILE *fp_ou
, int depth
)
1959 if(!ShowSargLogo
) return;
1960 fputs("<div class=\"logo\"><a href=\"http://sarg.sourceforge.net\"><img src=\"",fp_ou
);
1961 for (i
=0 ; i
<depth
; i
++)
1963 fputs("images/sarg.png\" title=\"SARG, Squid Analysis Report Generator. Logo by Osamu Matsuzaki\" alt=\"Sarg\"></a> Squid Analysis Report Generator</div>\n",fp_ou
);
1966 void write_logo_image(FILE *fp_ou
)
1968 if(LogoImage
[0]!='\0')
1969 fprintf(fp_ou
, "<div class=\"logo\"><img src=\"%s\" width=\"%s\" height=\"%s\" alt=\"Logo\"> %s</div>\n",LogoImage
,Width
,Height
,LogoText
);
1972 void write_html_head(FILE *fp_ou
, int depth
, const char *page_title
,int javascript
)
1976 fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n",fp_ou
);
1977 fprintf(fp_ou
, "<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet
);
1978 if (page_title
) fprintf(fp_ou
,"<title>%s</title>\n",page_title
);
1980 if ((javascript
& HTML_JS_SORTTABLE
)!=0 && SortTableJs
[0]) {
1981 fputs("<script type=\"text/javascript\" src=\"",fp_ou
);
1982 if (strncmp(SortTableJs
,"../",3)==0) {
1983 for (i
=0 ; i
<depth
; i
++) fputs("../",fp_ou
);
1985 fputs(SortTableJs
,fp_ou
);
1986 fputs("\"></script>\n",fp_ou
);
1988 fputs("</head>\n<body>\n",fp_ou
);
1991 void write_html_header(FILE *fp_ou
, int depth
, const char *page_title
,int javascript
)
1993 write_html_head(fp_ou
,depth
,page_title
,javascript
);
1994 write_logo_image(fp_ou
);
1995 show_sarg(fp_ou
, depth
);
1996 fprintf(fp_ou
,"<div class=\"title\"><table cellpadding=\"0\" cellspacing=\"0\">\n<tr><th class=\"title_c\">%s</th></tr>\n",Title
);
1999 void close_html_header(FILE *fp_ou
)
2001 fputs("</table></div>\n",fp_ou
);
2004 void write_html_trailer(FILE *fp_ou
)
2007 fputs("</body>\n</html>\n",fp_ou
);
2010 void output_html_string(FILE *fp_ou
,const char *str
,int maxlen
)
2014 while (*str
&& (maxlen
<=0 || i
<maxlen
)) {
2017 fputs("&",fp_ou
);
2020 fputs("<",fp_ou
);
2023 fputs(">",fp_ou
);
2026 fputs(""",fp_ou
);
2029 fputs("'",fp_ou
);
2037 if (maxlen
>0 && i
>=maxlen
)
2038 fputs("…",fp_ou
);
2041 void output_html_url(FILE *fp_ou
,const char *url
)
2045 fputs("&",fp_ou
);
2053 Write a host name inside an A tag of a HTML file. If the host name starts
2054 with a star, it is assumed to be an alias that cannot be put inside a link
2055 so the A tag is not written around the host name.
2057 \param fp_ou The handle of the HTML file.
2058 \param url The host to display in the HTML file.
2059 \param maxlen The maximum number of characters to print into the host name.
2061 void output_html_link(FILE *fp_ou
,const char *url
,int maxlen
)
2063 if (url
[0]==ALIAS_PREFIX
) {
2064 // this is an alias, no need for a A tag
2065 output_html_string(fp_ou
,url
+1,100);
2067 if (skip_scheme(url
)==url
)
2068 fputs("<a href=\"http://",fp_ou
);//no scheme in the url, assume http:// to make the link clickable
2070 fputs("<a href=\"",fp_ou
);//the scheme is in the url, no need to add one
2071 output_html_url(fp_ou
,url
);
2073 output_html_string(fp_ou
,url
,100);
2074 fputs("</a>",fp_ou
);
2078 void url_module(const char *url
, char *w2
)
2084 for(x
=strlen(url
)-1; x
>=0; x
--) {
2085 if(url
[x
] == '/' || y
>=sizeof(w
)-1) break;
2094 for(y
=y
-1; y
>=0; y
--) {
2101 Mangle an URL to produce a part that can be used as an anchor in
2102 a html <a name=""> tag.
2104 \param url The URL to mangle.
2105 \param anchor The buffer to write the mangled URL.
2106 \param size The size of the buffer.
2108 void url_to_anchor(const char *url
,char *anchor
,int size
)
2114 for (i
=0 ; url
[i
] && url
[i
]!='/' && url
[i
]!='?' ; i
++);
2121 // only keep really safe characters
2127 if(isalnum(url
[i
]) || url
[i
]=='-' || url
[i
]=='_' || url
[i
]=='.') {
2131 if (!skip
) anchor
[--j
]='_';
2149 printf(_("SARG Version: %s\n"),VERSION
);
2150 #if defined(ENABLE_NLS) && defined(HAVE_LOCALE_H)
2152 printf(_("\nFor the translation to work, a valid message file should be copied to "
2153 "\"%s/<Locale>/LC_MESSAGES/%s.mo\" where <Locale> is derived from the effective locale.\n"),LOCALEDIR
,PACKAGE_NAME
);
2154 if (CurrentLocale
) {
2155 printf(_("Currently effective locale is \"%s\".\n"),CurrentLocale
);
2157 printf(_("Locale is not set in the environment variable.\n"));
2159 // TRANSLATORS: You may change this message to tell the reader that the language is correctly supported.
2160 printf(_("If this message is in English, then your language is not supported or not correctly installed.\n"));
2165 printf(_("File globbing compiled in.\n"));
2167 printf(_("File globbing NOT compiled in.\n"));
2173 char *get_param_value(const char *param
,char *line
)
2177 while (*line
==' ' || *line
=='\t') line
++;
2179 if (strncasecmp(line
,param
,plen
)) return(NULL
);
2180 if (line
[plen
]!=' ' && line
[plen
]!='\t') return(NULL
);
2182 while (*line
==' ' || *line
=='\t') line
++;
2186 void unlinkdir(const char *dir
,bool contentonly
)
2190 struct dirent
*direntp
;
2196 while ((direntp
= readdir(dirp
)) != NULL
) {
2197 if (direntp
->d_name
[0] == '.' && (direntp
->d_name
[1] == '\0' ||
2198 (direntp
->d_name
[1] == '.' && direntp
->d_name
[2] == '\0')))
2200 if (snprintf(dname
,sizeof(dname
),"%s/%s",dir
,direntp
->d_name
)>=sizeof(dname
)) {
2201 debuga(__FILE__
,__LINE__
,_("Path too long: "));
2202 debuga_more("%s/%s\n",dir
,direntp
->d_name
);
2206 err
=lstat(dname
,&st
);
2208 err
=stat(dname
,&st
);
2211 debuga(__FILE__
,__LINE__
,_("Cannot stat \"%s\": %s\n"),dname
,strerror(errno
));
2214 if (S_ISREG(st
.st_mode
)) {
2215 if (unlink(dname
)) {
2216 debuga(__FILE__
,__LINE__
,_("Cannot delete \"%s\": %s\n"),dname
,strerror(errno
));
2219 } else if (S_ISDIR(st
.st_mode
)) {
2222 debuga(__FILE__
,__LINE__
,_("Don't know how to delete \"%s\" (not a regular file nor a directory)\n"),dname
);
2229 debuga(__FILE__
,__LINE__
,_("Cannot delete \"%s\": %s\n"),dir
,strerror(errno
));
2236 Delete every file from the temporary directory where sarg is told to store its
2239 As any stray file left over by a previous run would be included in the report, we
2240 must delete every file from the temporary directory before we start processing the logs.
2242 But the temporary directory is given by the user either in the configuration file or
2243 on the command line. We check that the user didn't give a wrong directory by looking
2244 at the files stored in the directory. If a single file is not one of ours, we abort.
2246 \param dir The temporary directory to purge.
2248 void emptytmpdir(const char *dir
)
2252 struct dirent
*direntp
;
2258 static const char *TmpExt
[]=
2276 // make sure the temporary directory contains only our files
2277 while ((direntp
= readdir(dirp
)) != NULL
) {
2278 if (direntp
->d_name
[0] == '.' && (direntp
->d_name
[1] == '\0' ||
2279 (direntp
->d_name
[1] == '.' && direntp
->d_name
[2] == '\0')))
2282 // is it one of our files
2283 dlen
=strlen(direntp
->d_name
);
2284 for (i
=sizeof(TmpExt
)/sizeof(TmpExt
[0])-1 ; i
>=0 ; i
--) {
2285 elen
=strlen(TmpExt
[i
]);
2286 if (dlen
>=elen
&& strcasecmp(direntp
->d_name
+dlen
-elen
,TmpExt
[i
])==0) break;
2289 debuga(__FILE__
,__LINE__
,_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2290 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2291 "directory or manually delete the content of \"%s\"\n"),direntp
->d_name
,dir
,dir
);
2295 if (snprintf(dname
,sizeof(dname
),"%s/%s",dir
,direntp
->d_name
)>=sizeof(dname
)) {
2296 debuga(__FILE__
,__LINE__
,_("Path too long: "));
2297 debuga_more("%s/%s\n",dir
,direntp
->d_name
);
2302 err
=lstat(dname
,&st
);
2304 err
=stat(dname
,&st
);
2307 debuga(__FILE__
,__LINE__
,_("Cannot stat \"%s\": %s\n"),dname
,strerror(errno
));
2310 if (!S_ISDIR(st
.st_mode
) && !S_ISREG(st
.st_mode
)) {
2311 debuga(__FILE__
,__LINE__
,_("Unknown path type for \"%s\". Check temporary directory\n"),dname
);
2317 // now delete our files
2318 while ((direntp
= readdir(dirp
)) != NULL
) {
2319 if (direntp
->d_name
[0] == '.' && (direntp
->d_name
[1] == '\0' ||
2320 (direntp
->d_name
[1] == '.' && direntp
->d_name
[2] == '\0')))
2323 // is it one of our files
2324 dlen
=strlen(direntp
->d_name
);
2325 for (i
=sizeof(TmpExt
)/sizeof(TmpExt
[0])-1 ; i
>=0 ; i
--) {
2326 elen
=strlen(TmpExt
[i
]);
2327 if (dlen
>=elen
&& strcasecmp(direntp
->d_name
+dlen
-elen
,TmpExt
[i
])==0) break;
2330 debuga(__FILE__
,__LINE__
,_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2331 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2332 "directory or manually delete the content of \"%s\"\n"),direntp
->d_name
,dir
,dir
);
2336 if (snprintf(dname
,sizeof(dname
),"%s/%s",dir
,direntp
->d_name
)>=sizeof(dname
)) {
2337 debuga(__FILE__
,__LINE__
,_("Path too long: "));
2338 debuga_more("%s/%s\n",dir
,direntp
->d_name
);
2342 err
=lstat(dname
,&st
);
2344 err
=stat(dname
,&st
);
2347 debuga(__FILE__
,__LINE__
,_("Cannot stat \"%s\": %s\n"),dname
,strerror(errno
));
2350 if (S_ISDIR(st
.st_mode
)) {
2352 } else if (S_ISREG(st
.st_mode
)) {
2353 if (unlink(dname
)) {
2354 debuga(__FILE__
,__LINE__
,_("Cannot delete \"%s\": %s\n"),dname
,strerror(errno
));
2358 debuga(__FILE__
,__LINE__
,_("Don't know how to delete \"%s\" (not a regular file)\n"),dname
);
2365 Extract an url, IPv4 or IPv6 from a buffer. The IP addresses may end with a
2368 \param buf The buffer to parse.
2369 \param text A pointer to set to the beginning of the string pattern. No terminating zero is inserted.
2370 The pointer may be NULL.
2371 \param ipv4 A 4 bytes buffer to store the bytes of the IPv4 address.
2372 \param ipv6 A 8 short integers buffer to store the values of the IPv6 address.
2373 \param nbits The number of prefix bits for an IP address.
2374 \param next The content of the line after the extracted address.
2376 \retval 3 The pattern is a IPv6 address.
2377 \retval 2 The pattern is a IPv4 address.
2378 \retval 1 The patter is a string.
2379 \retval 0 Empty pattern.
2381 int extract_address_mask(const char *buf
,const char **text
,unsigned char *ipv4
,unsigned short int *ipv6
,int *nbits
,const char **next
)
2386 unsigned int value4
, value6
;
2387 unsigned short int addr
[8];
2397 // skip leading spaces and tabs
2398 while (*buf
&& (*buf
==' ' || *buf
=='\t')) buf
++;
2400 // find out the nature of the pattern
2401 ip_size
=0x60 | 0x04;
2412 for (i
=0 ; (unsigned char)buf
[i
]>' ' && buf
[i
]!='/' && buf
[i
]!='?' && (!bracket
|| buf
[i
]!=']') && ip_size
; i
++) {
2413 if (ip_size
& 0x04) {
2414 if (isdigit(buf
[i
])) {
2416 port_num
=port_num
*10+(buf
[i
]-'0');
2417 if (port_num
>65535) ip_size
&=~0x04;
2419 value4
=value4
*10+(buf
[i
]-'0');
2420 if (value4
>0xFFU
) ip_size
&=~0x04;
2422 } else if (buf
[i
]=='.' && addr_len
<4) {
2423 addr
[addr_len
++]=(unsigned short)(value4
& 0xFFU
);
2425 } else if (!port
&& buf
[i
]==':') {
2431 if (ip_size
& 0x60) {
2432 if (isdigit(buf
[i
])) {
2433 value6
=(value6
<<4)+(buf
[i
]-'0');
2435 if (value6
>0xFFFFU
) ip_size
&=~0x60;
2436 } else if (toupper(buf
[i
])>='A' && toupper(buf
[i
])<='F') {
2437 value6
=(value6
<<4)+(toupper(buf
[i
])-'A'+10);
2439 if (value6
>0xFFFFU
) ip_size
&=~0x60;
2440 } else if (buf
[i
]==':' && addr_len
<8) {
2441 if (nibble6_len
>0) {
2442 addr
[addr_len
++]=(unsigned short)(value6
& 0xFFFFU
);
2446 if (buf
[i
+1]==':') {
2455 if (i
==0) return(0);
2456 if (ip_size
& 0x04) {
2460 addr
[addr_len
++]=(unsigned short)(value4
& 0xFFU
);
2462 if (ip_size
& 0x60) {
2463 if (pad_pos
<0 && addr_len
!=7) {
2465 } else if (pad_pos
>=0 && addr_len
>=7)
2467 else if (nibble6_len
>0)
2468 addr
[addr_len
++]=(unsigned short)(value6
& 0xFFFFU
);
2473 if (bracket
) (*text
)--;
2475 while ((unsigned char)buf
[i
]>' ') i
++;
2476 if (next
) *next
=buf
+i
;
2479 max_mask
=(ip_size
& 0x04) ? 4*8 : 8*16;
2483 while (isdigit(buf
[i
])) i
++;
2484 if (mask
<0 || mask
>max_mask
) mask
=max_mask
;
2487 if (ip_size
& 0x60 && bracket
&& buf
[i
]==']') i
++;
2488 if (next
) *next
=buf
+i
;
2489 if (ip_size
& 0x04) {
2490 if (nbits
) *nbits
=mask
;
2491 for (i
=0 ; i
<addr_len
; i
++)
2492 ipv4
[i
]=(unsigned char)addr
[i
];
2497 if (nbits
) *nbits
=mask
;
2502 ipv6
[j
++]=(unsigned short int)addr
[i
++];
2504 while (j
<pad_pos
+pad_len
)
2508 ipv6
[j
++]=(unsigned short int)addr
[i
++];