]>
git.ipfire.org Git - thirdparty/sarg.git/blob - util.c
095cf07e10329b8b6f4fb1d4fa133f87d08246ea
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(_("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(_("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
++) {
96 debuga(_("End of word not found in getword after %d bytes.\n"),x
);
97 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
98 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
99 debuga(_("searching for \'x%x\'\n"),stop
);
100 word
[(limit
>0) ? limit
-1 : 0]='\0';
101 #if USE_GETWORD_BACKTRACE
106 word
[x
] = gwarea
->current
[x
];
110 if (gwarea
->current
[x
]) ++x
;
115 int getword_limit(char *word
, int limit
, struct getwordstruct
*gwarea
, char stop
)
120 for(x
=0; x
<limit
&& gwarea
->current
[x
] && gwarea
->current
[x
] != stop
;x
++) {
121 word
[x
] = gwarea
->current
[x
];
125 while (*gwarea
->current
&& *gwarea
->current
!= stop
) gwarea
->current
++;
126 if (*gwarea
->current
) ++gwarea
->current
;
130 int getword_multisep(char *word
, int limit
, struct getwordstruct
*gwarea
, char stop
)
134 for(x
=0;((gwarea
->current
[x
]) && (gwarea
->current
[x
] != stop
));x
++) {
136 debuga(_("End of word not found in getword_multisep after %d bytes.\n"),x
);
137 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
138 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
139 debuga(_("searching for \'x%x\'\n"),stop
);
140 if (limit
>0) word
[limit
-1]='\0';
141 #if USE_GETWORD_BACKTRACE
144 //exit(EXIT_FAILURE);
147 word
[x
] = gwarea
->current
[x
];
151 while (gwarea
->current
[x
] && gwarea
->current
[x
]==stop
) ++x
;
156 int getword_skip(int limit
, struct getwordstruct
*gwarea
, char stop
)
160 for(x
=0;(gwarea
->current
[x
] && (gwarea
->current
[x
] != stop
));x
++) {
162 debuga(_("End of word not found in getword_skip after %d bytes.\n"),x
);
163 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
164 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
165 debuga(_("searching for \'x%x\'\n"),stop
);
166 #if USE_GETWORD_BACKTRACE
173 if (gwarea
->current
[x
]) ++x
;
178 int getword_atoll(long long int *number
, struct getwordstruct
*gwarea
, char stop
)
184 if (gwarea
->current
[0] == '-') {
187 } else if (gwarea
->current
[0] == '+') {
191 for(x
=0;isdigit(gwarea
->current
[x
]);x
++) {
192 digit
=gwarea
->current
[x
]-'0';
193 if (*number
>= (LLONG_MAX
-digit
)/10) {
195 TRANSLATORS: The first %s is the function name (in the source code) where the
196 overflow is detected.
198 debuga(_("Integer overflow detected in %s in line %s\n"),__func__
,gwarea
->beginning
);
201 *number
=(*number
* 10) + digit
;
203 if(gwarea
->current
[x
] && gwarea
->current
[x
]!=stop
) {
205 TRANSLATORS: The %s is the function name, in the source code, where the problem occured.
207 debuga(_("End of number not found in %s after %d bytes.\n"),__func__
,x
);
208 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
209 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
210 debuga(_("searching for \'x%x\'\n"),stop
);
211 #if USE_GETWORD_BACKTRACE
218 if (gwarea
->current
[x
]) ++x
;
223 int getword_atoi(int *number
, struct getwordstruct
*gwarea
, char stop
)
229 if (gwarea
->current
[0] == '-') {
232 } else if (gwarea
->current
[0] == '+') {
236 for(x
=0;isdigit(gwarea
->current
[x
]);x
++) {
237 digit
=gwarea
->current
[x
]-'0';
238 if (*number
> (INT_MAX
-digit
)/10) {
239 debuga(_("Integer overflow detected in %s in line %s\n"),__func__
,gwarea
->beginning
);
242 *number
=(*number
* 10) + digit
;
244 if(gwarea
->current
[x
] && gwarea
->current
[x
]!=stop
) {
245 debuga(_("End of number not found in %s after %d bytes.\n"),__func__
,x
);
246 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
247 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
248 debuga(_("searching for \'x%x\'\n"),stop
);
249 #if USE_GETWORD_BACKTRACE
256 if (gwarea
->current
[x
]) ++x
;
261 int getword_atol(long int *number
, struct getwordstruct
*gwarea
, char stop
)
267 if (gwarea
->current
[0] == '-') {
270 } else if (gwarea
->current
[0] == '+') {
274 for(x
=0;isdigit(gwarea
->current
[x
]);x
++) {
275 digit
=gwarea
->current
[x
]-'0';
276 if (*number
> (LONG_MAX
-digit
)/10) {
277 debuga(_("Integer overflow detected in %s in line %s\n"),__func__
,gwarea
->beginning
);
280 *number
=(*number
* 10) + digit
;
282 if(gwarea
->current
[x
] && gwarea
->current
[x
]!=stop
) {
283 debuga(_("End of number not found in %s after %d bytes.\n"),__func__
,x
);
284 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
285 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
286 debuga(_("searching for \'x%x\'\n"),stop
);
287 #if USE_GETWORD_BACKTRACE
294 if (gwarea
->current
[x
]) ++x
;
299 int getword_atolu(unsigned long int *number
, struct getwordstruct
*gwarea
, char stop
)
304 if (gwarea
->current
[0] == '-') {
305 debuga(_("getword_atolu got a negative number.\n"));
306 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
307 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
310 if (gwarea
->current
[0] == '+') {
314 for(x
=0;isdigit(gwarea
->current
[x
]);x
++) {
315 digit
=gwarea
->current
[x
]-'0';
316 if (*number
> (ULONG_MAX
-digit
)/10) {
317 debuga(_("Integer overflow detected in %s in line %s\n"),__func__
,gwarea
->beginning
);
320 *number
=(*number
* 10) + digit
;
322 if(gwarea
->current
[x
] && gwarea
->current
[x
]!=stop
) {
323 debuga(_("End of number not found in %s after %d bytes.\n"),__func__
,x
);
324 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
325 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
326 debuga(_("searching for \'x%x\'\n"),stop
);
327 #if USE_GETWORD_BACKTRACE
333 if (gwarea
->current
[x
]) ++x
;
339 int getword_ptr(char *orig_line
,char **word
, struct getwordstruct
*gwarea
, char stop
)
342 \note Why pass the original buffer to the function ? Because we must modify it to
343 insert the terminating ASCII zero for the word we return and that's not compatible
344 with getword_restart(). Moreover, getword_start() sometime works on constant strings
345 so this function require the original buffer to detect any missuse.
351 if (orig_line
&& orig_line
!=gwarea
->beginning
) {
352 debuga(_("Invalid buffer passed to getword_ptr\n"));
356 start
=(gwarea
->current
-gwarea
->beginning
);
357 if (word
&& orig_line
) *word
=orig_line
+start
;
358 for(x
=0;((gwarea
->current
[x
]) && (gwarea
->current
[x
] != stop
));x
++);
359 sep
=(gwarea
->current
[x
]!='\0');
360 if (word
&& orig_line
) orig_line
[start
+x
] = '\0';
367 #define MAXLLL 30 //!< Maximum number of digits in long long (a guess).
368 long long int my_atoll (const char *nptr
)
370 long long int returnval
=0LL;
371 int max_digits
= MAXLLL
;
373 // Soak up all the white space
374 while (isspace( *nptr
)) {
378 //For each character left to right
379 //change the character to a single digit
380 //multiply what we had before by 10 and add the new digit
382 while (--max_digits
&& isdigit( *nptr
))
384 returnval
= ( returnval
* 10 ) + ( *nptr
++ - '0' ) ;
390 int is_absolute(const char *path
)
392 if (*path
=='/') return(1);
394 if (isalpha(path
[0]) && path
[1]==':') return(1);
399 int PortableMkDir(const char *path
,int mode
)
401 #if defined(__linux__)
402 int mkerror
=mkdir(path
,mode
);
405 int mkerror
=_mkdir(path
);
410 void my_mkdir(const char *name
)
416 if(!is_absolute(name
)) {
417 debuga(_("Invalid path (%s). Please, use absolute paths only.\n"),name
);
422 for (i
=0 ; name
[i
] ; i
++) {
424 debuga(_("Path too long: "));
425 debuga_more("%s\n",name
);
428 if (chars
>0 && name
[i
] == '/') {
430 if (access(w0
, R_OK
) != 0) {
431 if (PortableMkDir(w0
,0755)) {
432 debuga(_("Cannot create directory \"%s\": %s\n"),w0
,strerror(errno
));
437 if (name
[i
] != '/') chars
++;
441 if (access(name
, R_OK
) != 0) {
442 if (PortableMkDir(name
,0755)) {
443 debuga(_("Cannot create directory \"%s\": %s\n"),name
,strerror(errno
));
450 void my_lltoa(unsigned long long int n
, char *s
, int ssize
, int len
)
459 debuga(_("The requested number of digits passed to my_lltoa (%d) is bigger than the output buffer size (%d)\n"),len
,ssize
);
464 s
[slen
++] = (n
% 10) + '0';
465 } while ((n
/= 10) > 0 && slen
<ssize
);
468 for (i
= 0, j
= slen
-1; i
<j
; i
++, j
--) {
476 for(j
=slen
; j
>=0; j
--)
483 int month2num(const char *month
)
487 for(m
=0 ; m
<12 && strcmp(mtab1
[m
],month
) != 0; m
++);
491 int builddia(int day
, int month
, int year
)
493 return(year
*10000+month
*100+day
);
499 \param date1 The first date to compare.
500 \param date2 The second date to compare.
502 \retval -1 If date1<date2.
503 \retval 0 If date1==date2.
504 \retval 1 if date1>date2.
506 int compare_date(struct tm
*date1
,struct tm
*date2
)
508 if (date1
->tm_year
<date2
->tm_year
) return(-1);
509 if (date1
->tm_year
>date2
->tm_year
) return(1);
510 if (date1
->tm_mon
<date2
->tm_mon
) return(-1);
511 if (date1
->tm_mon
>date2
->tm_mon
) return(1);
512 if (date1
->tm_mday
<date2
->tm_mday
) return(-1);
513 if (date1
->tm_mday
>date2
->tm_mday
) return(1);
514 if (date1
->tm_hour
<date2
->tm_hour
) return(-1);
515 if (date1
->tm_hour
>date2
->tm_hour
) return(1);
516 if (date1
->tm_min
<date2
->tm_min
) return(-1);
517 if (date1
->tm_min
>date2
->tm_min
) return(1);
518 if (date1
->tm_sec
<date2
->tm_sec
) return(-1);
519 if (date1
->tm_sec
>date2
->tm_sec
) return(1);
523 void buildymd(const char *dia
, const char *mes
, const char *ano
, char *wdata
,int wdata_size
)
528 snprintf(wdata
,wdata_size
,"%04d%02d%02d",atoi(ano
),nmes
+1,atoi(dia
));
532 int conv_month(const char *month
)
536 for(x
=0; x
<12 && strncmp(mtab1
[x
],month
,3)!=0; x
++);
541 const char *conv_month_name(int month
)
545 if (month
<1 || month
>12) {
546 snprintf(str
,sizeof(str
),"%03d",month
);
549 return(mtab1
[month
-1]);
553 Write a debug message to stderr. The message is prefixed by "SARG:" to identify its origin.
555 \param msg The printf like message to format.
556 \param ... The arguments to format in the message.
558 void debuga(const char *msg
,...)
562 fputs(_("SARG: "),stderr
);
564 vfprintf(stderr
,msg
,ap
);
569 Write a debug message to stderr. The message is supposed
570 to be displayed after a message from debuga().
572 \param msg The printf like message to format.
573 \param ... The arguments to format in the message.
575 void debuga_more(const char *msg
,...)
580 vfprintf(stderr
,msg
,ap
);
585 Write a debug message to stderr. The message is prefixed by "SARG: (info)".
587 \param msg The printf like message to format.
588 \param ... The arguments to format in the message.
590 void debugaz(const char *msg
,...)
594 fputs(_("SARG: (info) "),stderr
);
596 vfprintf(stderr
,msg
,ap
);
601 char *fixnum(long long int value
, int n
)
603 #define MAXLEN_FIXNUM 256
604 char num
[MAXLEN_FIXNUM
]="";
605 char buf
[MAXLEN_FIXNUM
* 2];
607 static char ret
[MAXLEN_FIXNUM
* 2];
609 register int i
, j
, k
;
611 static char abbrev
[30]="";
613 my_lltoa(value
, num
, sizeof(num
), 0);
615 if(DisplayedValues
==DISPLAY_ABBREV
) {
616 numlen
= strlen(num
);
619 else if (numlen
%3 == 1) {
621 abbrev
[1]=(UseComma
) ? ',' : '.';
626 else if (numlen
%3 == 2) {
629 abbrev
[2]=(UseComma
) ? ',' : '.';
634 else if (numlen
%3 == 0) {
638 abbrev
[3]=(UseComma
) ? ',' : '.';
647 else if (numlen
<= 6)
649 else if (numlen
<= 9)
651 else if (numlen
<= 12)
653 else if (numlen
<= 15)
655 else if (numlen
>= 18)
657 else if (numlen
<= 21)
659 else if (numlen
<= 24)
661 else if (numlen
<= 27)
664 strcat(abbrev
,"???");
669 memset(buf
,0,MAXLEN_FIXNUM
*2);
675 for ( i
= strlen(num
) - 1, j
= 0 ; i
> -1; i
--) {
676 if ( k
== 2 && i
!= 0 ) {
679 pbuf
[j
++] = (UseComma
) ? ',' : '.';
689 for ( i
= strlen(pbuf
) - 1, j
= 0 ; i
> -1; i
--, j
++)
698 char *fixnum2(long long int value
, int n
)
700 #define MAXLEN_FIXNUM2 1024
701 char num
[MAXLEN_FIXNUM2
];
702 char buf
[MAXLEN_FIXNUM2
* 2];
704 static char ret
[MAXLEN_FIXNUM2
* 2];
706 register int i
, j
, k
;
708 my_lltoa(value
, num
, sizeof(num
), 0);
709 memset(buf
,0,MAXLEN_FIXNUM2
*2);
715 for ( i
= strlen(num
) - 1, j
= 0 ; i
> -1; i
--) {
716 if ( k
== 2 && i
!= 0 ) {
719 pbuf
[j
++] = (UseComma
) ? ',' : '.';
729 for ( i
= strlen(pbuf
) - 1, j
= 0 ; i
> -1; i
--, j
++)
738 char *buildtime(long long int elap
)
740 long int num
= elap
/ 1000LL;
747 min
=(num
% 3600L) / 60L;
749 snprintf(buf
,sizeof(buf
),"%02d:%02d:%02d",hor
,min
,sec
);
756 Get the date stored in the <tt>sarg-date</tt> file of a directory with the connection data.
758 \param dirname The directory to look for the connection directory.
759 \param name The name of the directory whose <tt>sarg-date</tt> file must be read.
760 \param data The buffer to store the content of the file. It must be more than 80
764 \retval -1 File not found.
766 int obtdate(const char *dirname
, const char *name
, char *data
)
771 if (snprintf(wdir
,sizeof(wdir
),"%s%s/sarg-date",dirname
,name
)>=sizeof(wdir
)) {
772 debuga(_("Buffer too small to store "));
773 debuga_more("%s%s/sarg-date",dirname
,name
);
776 if ((fp_in
= fopen(wdir
, "rt")) == 0) {
777 if (snprintf(wdir
,sizeof(wdir
),"%s%s/date",dirname
,name
)>=sizeof(wdir
)) {
778 debuga(_("Buffer too small to store "));
779 debuga_more("%s%s/date",dirname
,name
);
782 if ((fp_in
= fopen(wdir
, "rt")) == 0) {
788 if (!fgets(data
,80,fp_in
)) {
789 debuga(_("Failed to read the date in %s\n"),wdir
);
799 void formatdate(char *date
,int date_size
,int year
,int month
,int day
,int hour
,int minute
,int second
,int dst
)
805 memset(<m
,0,sizeof(ltm
));
806 if (year
>=1900) ltm
.tm_year
=year
-1900;
807 if (month
>=1 && month
<=12) ltm
.tm_mon
=month
-1;
808 if (day
>=1 && day
<=31) ltm
.tm_mday
=day
;
809 if (hour
>=0 && hour
<24) ltm
.tm_hour
=hour
;
810 if (minute
>=0 && minute
<60) ltm
.tm_min
=minute
;
811 if (second
>=0 && second
<60) ltm
.tm_sec
=second
;
813 unixtime
=mktime(<m
); //fill the missing entries
814 fulltm
=localtime(&unixtime
);
815 //strftime(date,date_size,"%a %b %d %H:%M:%S %Z %Y",fulltm);
816 strftime(date
,date_size
,"%c",fulltm
);
820 void computedate(int year
,int month
,int day
,struct tm
*t
)
822 memset(t
,0,sizeof(*t
));
823 t
->tm_year
=year
-1900;
829 int obtuser(const char *dirname
, const char *name
)
836 if (snprintf(wdir
,sizeof(wdir
),"%s%s/sarg-users",dirname
,name
)>=sizeof(wdir
)) {
837 debuga(_("Buffer too small to store "));
838 debuga_more("%s%s/sarg-users",dirname
,name
);
841 if((fp_in
=fopen(wdir
,"r"))==NULL
) {
842 if (snprintf(wdir
,sizeof(wdir
),"%s%s/users",dirname
,name
)>=sizeof(wdir
)) {
843 debuga(_("Buffer too small to store "));
844 debuga_more("%s%s/users",dirname
,name
);
847 if((fp_in
=fopen(wdir
,"r"))==NULL
) {
852 if (!fgets(tuser
,sizeof(tuser
),fp_in
)) {
853 debuga(_("Failed to read the number of users in %s\n"),wdir
);
863 void obttotal(const char *dirname
, const char *name
, int nuser
, long long int *tbytes
, long long int *media
)
868 char user
[MAX_USER_LEN
];
870 struct getwordstruct gwarea
;
876 if (snprintf(wdir
,sizeof(wdir
),"%s%s/sarg-general",dirname
,name
)>=sizeof(wdir
)) {
877 debuga(_("Buffer too small to store "));
878 debuga_more("%s%s/sarg-general",dirname
,name
);
881 if ((fp_in
= fopen(wdir
, "r")) == 0) {
882 if (snprintf(wdir
,sizeof(wdir
),"%s%s/general",dirname
,name
)>=sizeof(wdir
)) {
883 debuga(_("Buffer too small to store "));
884 debuga_more("%s%s/general",dirname
,name
);
887 if ((fp_in
= fopen(wdir
, "r")) == 0) {
892 if ((line
=longline_create())==NULL
) {
893 debuga(_("Not enough memory to read the file %s\n"),wdir
);
897 while((buf
=longline_read(fp_in
,line
))!=NULL
) {
898 if (strncmp(buf
,"TOTAL\t",6) == 0)
900 else if (strncmp(buf
,"TOTAL ",6) == 0)
904 getword_start(&gwarea
,buf
);
905 if (getword(user
,sizeof(user
),&gwarea
,sep
)<0) {
906 debuga(_("There is a invalid user in file %s\n"),wdir
);
909 if(strcmp(user
,"TOTAL") != 0)
911 if (getword_skip(MAXLEN
,&gwarea
,sep
)<0) {
912 debuga(_("There a broken total number of access in file %s\n"),wdir
);
915 if (getword_atoll(tbytes
,&gwarea
,sep
)<0) {
916 debuga(_("There is a broken number of bytes in file %s\n"),wdir
);
922 longline_destroy(&line
);
927 *media
=*tbytes
/ nuser
;
931 int getperiod_fromsarglog(const char *arqtt
,struct periodstruct
*period
)
934 int day0
, month0
, year0
, hour0
, minute0
;
935 int day1
, month1
, year1
, hour1
, minute1
;
938 memset(period
,0,sizeof(*period
));
941 while((str
=strstr(str
,"sarg-"))!=NULL
) {
943 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
944 day0
=(str
[0]-'0')*10+(str
[1]-'0');
945 if (day0
<1 || day0
>31) continue;
947 month0
=(str
[0]-'0')*10+(str
[1]-'0')-1;
948 if (month0
<0 || month0
>11) continue;
951 for (i
=0 ; isdigit(str
[i
]) && i
<4 ; i
++) year0
=year0
*10+(str
[i
]-'0');
954 if (str
[0]!='_') continue;
957 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
958 hour0
=(str
[0]-'0')*10+(str
[1]-'0');
960 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
961 minute0
=(str
[0]-'0')*10+(str
[1]-'0');
964 if (*str
!= '-') continue;
967 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
968 day1
=(str
[0]-'0')*10+(str
[1]-'0');
969 if (day1
<1 || day1
>31) continue;
971 month1
=(str
[0]-'0')*10+(str
[1]-'0')-1;
972 if (month1
<0 || month1
>11) continue;
975 for (i
=0 ; isdigit(str
[i
]) && i
<4 ; i
++) year1
=year1
*10+(str
[i
]-'0');
979 if (str
[0]!='_') continue;
982 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
983 hour1
=(str
[0]-'0')*10+(str
[1]-'0');
985 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
986 minute1
=(str
[0]-'0')*10+(str
[1]-'0');
989 period
->start
.tm_mday
=day0
;
990 period
->start
.tm_mon
=month0
;
991 period
->start
.tm_year
=year0
-1900;
992 period
->start
.tm_hour
=hour0
;
993 period
->start
.tm_min
=minute0
;
994 period
->end
.tm_mday
=day1
;
995 period
->end
.tm_mon
=month1
;
996 period
->end
.tm_year
=year1
-1900;
997 period
->end
.tm_hour
=hour1
;
998 period
->end
.tm_min
=minute1
;
1004 void getperiod_fromrange(struct periodstruct
*period
,int dfrom
,int duntil
)
1006 memset(&period
->start
,0,sizeof(period
->start
));
1007 period
->start
.tm_mday
=dfrom
%100;
1008 period
->start
.tm_mon
=(dfrom
/100)%100-1;
1009 period
->start
.tm_year
=(dfrom
/10000)-1900;
1011 memset(&period
->end
,0,sizeof(period
->end
));
1012 period
->end
.tm_mday
=duntil
%100;
1013 period
->end
.tm_mon
=(duntil
/100)%100-1;
1014 period
->end
.tm_year
=(duntil
/10000)-1900;
1018 Update the \a main period to encompass the period in \a candidate.
1020 void getperiod_merge(struct periodstruct
*main
,struct periodstruct
*candidate
)
1025 mdate
=(main
->start
.tm_year
)*10000+(main
->start
.tm_mon
)*100+main
->start
.tm_mday
;
1026 cdate
=(candidate
->start
.tm_year
)*10000+(candidate
->start
.tm_mon
)*100+candidate
->start
.tm_mday
;
1027 if (cdate
<mdate
) memcpy(&main
->start
,&candidate
->start
,sizeof(struct tm
));
1029 mdate
=(main
->end
.tm_year
)*10000+(main
->end
.tm_mon
)*100+main
->end
.tm_mday
;
1030 cdate
=(candidate
->end
.tm_year
)*10000+(candidate
->end
.tm_mon
)*100+candidate
->end
.tm_mday
;
1031 if (cdate
>mdate
) memcpy(&main
->end
,&candidate
->end
,sizeof(struct tm
));
1034 int getperiod_buildtext(struct periodstruct
*period
)
1038 char text1
[40], text2
[40];
1041 i
=strftime(text1
, sizeof(text1
), "%Y %b %d", &period
->start
);
1042 } else if(df
=='e') {
1043 i
=strftime(text1
, sizeof(text1
), "%d %b %Y", &period
->start
);
1044 } else /*if (df=='w')*/ {
1045 IndexTree
=INDEX_TREE_FILE
;
1046 i
=strftime(text1
, sizeof(text1
), "%Y.%U", &period
->start
);
1048 if (i
== 0) return(-1);
1050 range
=(period
->start
.tm_year
!=period
->end
.tm_year
||
1051 period
->start
.tm_mon
!=period
->end
.tm_mon
||
1052 period
->start
.tm_mday
!=period
->end
.tm_mday
);
1055 i
=strftime(text2
, sizeof(text2
)-i
, "%Y %b %d", &period
->end
);
1056 } else if (df
=='e') {
1057 i
=strftime(text2
, sizeof(text2
)-i
, "%d %b %Y", &period
->end
);
1059 i
=strftime(text2
, sizeof(text2
)-i
, "%Y.%U", &period
->end
);
1061 if (i
== 0) return(-1);
1065 snprintf(period
->text
,sizeof(period
->text
),"%s-%s",text1
,text2
);
1066 snprintf(period
->html
,sizeof(period
->html
),"%s—%s",text1
,text2
);
1068 safe_strcpy(period
->text
,text1
,sizeof(period
->text
));
1069 safe_strcpy(period
->html
,text1
,sizeof(period
->html
));
1074 static void copy_images(void)
1076 FILE *img_in
, *img_ou
;
1078 char srcfile
[MAXLEN
];
1079 char dstfile
[MAXLEN
];
1081 struct dirent
*direntp
;
1082 char buffer
[MAXLEN
];
1086 if (snprintf(images
,sizeof(images
),"%simages",outdir
)>=sizeof(images
)) {
1087 debuga(_("Cannot copy images to target directory %simages\n"),outdir
);
1090 if (access(images
,R_OK
)!=0) {
1091 if (PortableMkDir(images
,0755)) {
1092 debuga(_("Cannot create directory \"%s\": %s\n"),images
,strerror(errno
));
1097 dirp
= opendir(ImageDir
);
1099 debuga(_("Cannot open directory \"%s\": %s\n"),ImageDir
,strerror(errno
));
1102 while ((direntp
= readdir( dirp
)) != NULL
){
1103 if(direntp
->d_name
[0]=='.')
1105 if (snprintf(srcfile
,sizeof(srcfile
),"%s/%s",ImageDir
,direntp
->d_name
)>=sizeof(srcfile
)) {
1106 debuga(_("Buffer too small to store "));
1107 debuga_more("%s/%s",ImageDir
,direntp
->d_name
);
1110 if (stat(srcfile
,&info
)) {
1111 debuga(_("Cannot stat \"%s\": %s\n"),srcfile
,strerror(errno
));
1114 if (S_ISREG(info
.st_mode
)) {
1115 if (snprintf(dstfile
,sizeof(dstfile
),"%s/%s",images
,direntp
->d_name
)>=sizeof(dstfile
)) {
1116 debuga(_("Buffer too small to store "));
1117 debuga_more("%s/%s",images
,direntp
->d_name
);
1120 img_in
= fopen(srcfile
, "rb");
1122 img_ou
= fopen(dstfile
, "wb");
1124 while ((nread
= fread(buffer
,1,sizeof(buffer
),img_in
))>0) {
1125 if (fwrite(buffer
,1,nread
,img_ou
)!=nread
) {
1126 debuga(_("Failed to copy image %s to %s\n"),srcfile
,dstfile
);
1130 if (fclose(img_ou
)==EOF
) {
1131 debuga(_("Error while copying image %s: %s\n"),dstfile
,strerror(errno
));
1135 debuga(_("Cannot open file \"%s\": %s\n"), dstfile
, strerror(errno
));
1138 debuga(_("Cannot open file \"%s\": %s\n"), srcfile
, strerror(errno
));
1141 (void) closedir(dirp
);
1147 * Check if the proposed file name conforms to the directory structure layed out
1148 * as a file tree. It is used to check if the file name enumerated while scanning
1149 * a directory content may have been created by sarg running with IndexTree set to
1152 bool IsTreeFileDirName(const char *Name
)
1157 // start year (date format u) or start day (date format e)
1158 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1160 if (isdigit(Name
[2]) && isdigit(Name
[3]))
1162 // date format is either u or w
1166 if (!isdigit(Name
[5]) || !isdigit(Name
[6])) return(false);
1167 return(true);//date format w is confirmed
1174 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1175 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1176 if (i
<0) return(false);
1180 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1185 else if (isalpha(Name
[2]) && isalpha(Name
[3]) && isalpha(Name
[4]))
1191 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1192 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1193 if (i
<0) return(false);
1197 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1205 if (Name
[0]!='-') return(false);
1208 if (DateFormat
=='u')
1210 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1213 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1214 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1215 if (i
<0) return(false);
1218 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1221 else //DateFormat=='e'
1223 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1226 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1227 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1228 if (i
<0) return(false);
1231 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1235 * The directory name may contains additional characters such as a counter if
1236 * a previous report is never overwritten.
1242 * Check if the proposed file name can be the year part of a report tree build with
1243 * IndexTree set to INDEX_TREE_DATE.
1245 bool IsTreeYearFileName(const char *Name
)
1247 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1252 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1255 if (Name
[0]) return(false);
1260 * Check if the proposed file name can be the month part of a report tree build with
1261 * IndexTree set to INDEX_TREE_DATE.
1263 bool IsTreeMonthFileName(const char *Name
)
1267 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1268 m
=(Name
[0]-'0')*10+(Name
[1]-'0');
1269 if (m
<1 || m
>12) return(false);
1274 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1275 m
=(Name
[0]-'0')*10+(Name
[1]-'0');
1276 if (m
<1 || m
>12) return(false);
1279 if (Name
[0]) return(false);
1284 * Check if the proposed file name can be the day part of a report tree build with
1285 * IndexTree set to INDEX_TREE_DATE.
1287 bool IsTreeDayFileName(const char *Name
)
1291 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1292 d
=(Name
[0]-'0')*10+(Name
[1]-'0');
1293 if (d
<1 || d
>31) return(false);
1297 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1298 d
=(Name
[0]-'0')*10+(Name
[1]-'0');
1299 if (d
<1 || d
>31) return(false);
1302 * The directory name may contains additional characters such as a counter if
1303 * a previous report is never overwritten.
1308 int vrfydir(const struct periodstruct
*per1
, const char *addr
, const char *site
, const char *us
, const char *form
)
1313 char dirname2
[MAXLEN
];
1321 strcpy(wdir
,outdir
);
1323 y1
=per1
->start
.tm_year
+1900;
1324 y2
=per1
->end
.tm_year
+1900;
1325 m1
=per1
->start
.tm_mon
+1;
1326 m2
=per1
->end
.tm_mon
+1;
1327 d1
=per1
->start
.tm_mday
;
1328 d2
=per1
->end
.tm_mday
;
1329 if(IndexTree
== INDEX_TREE_DATE
) {
1330 wlen
+=sprintf(wdir
+wlen
,"%04d",y1
);
1331 if(y1
!=y2
) wlen
+=sprintf(wdir
+wlen
,"-%04d",y2
);
1332 if(access(wdir
, R_OK
) != 0)
1335 wlen
+=sprintf(wdir
+wlen
,"/%02d",m1
);
1336 if(m1
!= m2
) wlen
+=sprintf(wdir
+wlen
,"-%02d",m2
);
1337 if(access(wdir
, R_OK
) != 0)
1340 wlen
+=sprintf(wdir
+wlen
,"/%02d",d1
);
1341 if(d1
!=d2
) wlen
+=sprintf(wdir
+wlen
,"-%02d",d2
);
1344 wlen
=snprintf(wdir
+wlen
,sizeof(wdir
)-wlen
,"%04d%s%02d-%04d%s%02d",y1
,
1345 conv_month_name(m1
),d1
,y2
,conv_month_name(m2
),d2
);
1346 } else if (df
== 'e') {
1347 wlen
=snprintf(wdir
+wlen
,sizeof(wdir
)-wlen
,"%02d%s%04d-%02d%s%04d",d1
,
1348 conv_month_name(m1
),y1
,d2
,conv_month_name(m2
),y2
);
1349 } else if (df
== 'w') {
1350 wlen2
=strftime(wdir
+wlen
, sizeof(wdir
)-wlen
, "%Y.%U", &per1
->start
);
1351 if (wlen2
==0) return(-1);
1357 struct userinfostruct
*uinfo
=userinfo_find_from_id(us
);
1360 strcat(wdir
,uinfo
->filename
);
1363 if(addr
[0] != '\0') {
1367 if(site
[0] != '\0') {
1372 strcpy(outdirname
,wdir
);
1374 if(IndexTree
!= INDEX_TREE_DATE
) {
1375 if(!OverwriteReport
) {
1377 if(access(wdir
,R_OK
) == 0) {
1378 sprintf(wdir
,"%s.%d",outdirname
,num
);
1387 debuga(_("File %s already exists, moved to %s\n"),outdirname
,wdir
);
1388 rename(outdirname
,wdir
);
1391 if(access(outdirname
,R_OK
) == 0) {
1392 unlinkdir(outdirname
,1);
1395 my_mkdir(outdirname
);
1397 strcpy(dirname2
,wdir
);
1398 if(!OverwriteReport
) {
1400 if(access(wdir
,R_OK
) == 0) {
1401 sprintf(wdir
,"%s.%d",dirname2
,num
);
1410 debuga(_("File %s already exists, moved to %s\n"),dirname2
,wdir
);
1411 rename(dirname2
,wdir
);
1412 strcpy(dirname2
,wdir
);
1415 if(access(wdir
,R_OK
) == 0) {
1420 if(access(wdir
, R_OK
) != 0)
1424 strcpy(dirname2
,wdir
);
1426 if (snprintf(wdir
,sizeof(wdir
),"%s/sarg-date",outdirname
)>=sizeof(wdir
)) {
1427 debuga(_("Buffer too small to store "));
1428 debuga_more("%s/sarg-date",outdirname
);
1431 if ((fp_ou
= fopen(wdir
, "wt")) == 0) {
1432 debuga(_("cannot open %s for writing: %s\n"),wdir
,strerror(errno
));
1437 //strftime(wdir,sizeof(wdir),"%a %b %d %H:%M:%S %Z %Y",localtime(&curtime));
1438 loctm
=localtime(&curtime
);
1439 strftime(wdir
,sizeof(wdir
),"%Y-%m-%d %H:%M:%S",loctm
);
1440 if (fprintf(fp_ou
,"%s %d\n",wdir
,loctm
->tm_isdst
)<0) {
1441 debuga(_("Failed to write the date in %s\n"),wdir
);
1445 if (fclose(fp_ou
)==EOF
) {
1446 debuga(_("Failed to write the date in %s: %s\n"),wdir
,strerror(errno
));
1455 Copy a string without overflowing the buffer. The copied string
1456 is properly terminated by an ASCII zero.
1458 \param dest The destination buffer.
1459 \param src The source buffer.
1460 \param length The size of the destination buffer. The program is aborted
1461 if the length is negative or zero.
1463 void safe_strcpy(char *dest
,const char *src
,int length
)
1466 debuga(_("Invalid buffer length passed to the function to safely copy a string\n"));
1469 strncpy(dest
,src
,length
-1);
1470 dest
[length
-1]='\0';
1473 void strip_latin(char *line
)
1480 for (i
=0;line
[i
];i
++){
1482 if (line
[i
]==';') skip
=0;
1494 void zdate(char *ftime
,int ftimesize
, char DateFormat
)
1500 local
= localtime(&t
);
1501 if (DateFormat
=='u')
1502 strftime(ftime
, ftimesize
, "%b/%d/%Y %H:%M", local
);
1503 else if (DateFormat
=='e')
1504 strftime(ftime
, ftimesize
, "%d/%b/%Y-%H:%M", local
);
1505 else if (DateFormat
=='w')
1506 strftime(ftime
, ftimesize
, "%W-%H-%M", local
);
1511 char *fixtime(long long int elap
)
1513 long int num
= elap
/ 1000LL;
1517 static char buf
[20];
1520 min
=(num
% 3600L) / 60L;
1523 if(hor
==0 && min
==0 && sec
==0)
1526 snprintf(buf
,sizeof(buf
),"%d:%02d:%02d",hor
,min
,sec
);
1532 void date_from(char *date
,int date_size
, int *dfrom
, int *duntil
)
1541 if (isdigit(date
[0])) {
1544 if (sscanf(date
,"%d/%d/%d%n",&d0
,&m0
,&y0
,&next
)!=3 || y0
<100 || m0
<1 || m0
>12 || d0
<1 || d0
>31 || next
<0) {
1545 debuga(_("The date passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1548 if (date
[next
]=='-') {
1549 if (sscanf(date
+next
+1,"%d/%d/%d",&d1
,&m1
,&y1
)!=3 || y1
<100 || m1
<1 || m1
>12 || d1
<1 || d1
>31) {
1550 debuga(_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1553 } else if (date
[next
]!='\0') {
1554 debuga(_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1564 struct tm
*Date0
,Date1
;
1566 if (time(&Today
)==(time_t)-1) {
1567 debuga(_("Failed to get the current time\n"));
1570 if (sscanf(date
,"day-%d",&i
)==1) {
1572 debuga(_("Invalid number of days in -d parameter\n"));
1576 Date0
=localtime(&Today
);
1578 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1581 y0
=y1
=Date0
->tm_year
+1900;
1582 m0
=m1
=Date0
->tm_mon
+1;
1583 d0
=d1
=Date0
->tm_mday
;
1584 } else if (sscanf(date
,"week-%d",&i
)==1) {
1586 There is no portable way to find the first day of the week even though the
1587 information is available in the locale. nl_langinfo has the unofficial
1588 parameters _NL_TIME_FIRST_WEEKDAY and _NL_TIME_WEEK_1STDAY but they are
1589 undocumented as is their return value and it is discouraged to use them.
1590 Beside, nl_langinfo isn't available on windows and the first day of the
1591 week isn't available at all on that system.
1593 const int FirstWeekDay
=1;
1597 debuga(_("Invalid number of weeks in -d parameter\n"));
1600 Date0
=localtime(&Today
);
1602 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1605 WeekBegin
=Today
-((Date0
->tm_wday
-FirstWeekDay
+7)%7)*24*60*60;
1606 WeekBegin
-=i
*7*24*60*60;
1607 Date0
=localtime(&WeekBegin
);
1609 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1612 y0
=Date0
->tm_year
+1900;
1615 WeekBegin
+=6*24*60*60;
1616 Date0
=localtime(&WeekBegin
);
1618 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1621 y1
=Date0
->tm_year
+1900;
1624 } else if (sscanf(date
,"month-%d",&i
)==1) {
1626 debuga(_("Invalid number of months in -d parameter\n"));
1629 Date0
=localtime(&Today
);
1631 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1634 if (Date0
->tm_mon
<i
%12) {
1635 y0
=Date0
->tm_year
+1900-i
/12-1;
1636 m0
=(Date0
->tm_mon
+12-i
%12)%12+1;
1639 y0
=Date0
->tm_year
+1900-i
/12;
1640 m0
=Date0
->tm_mon
-i
%12+1;
1643 memcpy(&Date1
,Date0
,sizeof(struct tm
));
1648 Date1
.tm_year
=y0
-1900;
1651 Date1
.tm_year
=y0
-1900+1;
1655 Date0
=localtime(&t1
);
1656 y1
=Date0
->tm_year
+1900;
1660 debuga(_("Invalid date range passed on command line\n"));
1665 *dfrom
=y0
*10000+m0
*100+d0
;
1666 *duntil
=y1
*10000+m1
*100+d1
;
1667 snprintf(date
,date_size
,"%02d/%02d/%04d-%02d/%02d/%04d",d0
,m0
,y0
,d1
,m1
,y1
);
1672 char *strlow(char *string
)
1678 for (s
= string
; *s
; ++s
)
1688 char *strup(char *string
)
1694 for (s
= string
; *s
; ++s
)
1702 void removetmp(const char *outdir
)
1707 if(!RemoveTempFiles
)
1711 debuga(_("Purging temporary file sarg-general\n"));
1713 if (snprintf(filename
,sizeof(filename
),"%s/sarg-general",outdir
)>=sizeof(filename
)) {
1714 debuga(_("Path too long: "));
1715 debuga_more("%s/sarg-period\n",outdir
);
1718 if((fp_gen
=fopen(filename
,"w"))==NULL
){
1719 debuga(_("Cannot open file \"%s\": %s\n"),filename
,strerror(errno
));
1722 totalger(fp_gen
,filename
);
1723 if (fclose(fp_gen
)==EOF
) {
1724 debuga(_("Failed to close %s after writing the total line: %s\n"),filename
,strerror(errno
));
1729 void load_excludecodes(const char *ExcludeCodes
)
1737 if(ExcludeCodes
[0] == '\0')
1740 if((fp_in
=fopen(ExcludeCodes
,"r"))==NULL
) {
1741 debuga(_("Cannot open file \"%s\": %s\n"),ExcludeCodes
,strerror(errno
));
1745 if (fseek(fp_in
, 0, SEEK_END
)==-1) {
1746 debuga(_("Failed to move till the end of the excluded codes file %s: %s\n"),ExcludeCodes
,strerror(errno
));
1749 MemSize
= ftell(fp_in
);
1751 debuga(_("Cannot get the size of file %s\n"),ExcludeCodes
);
1754 if (fseek(fp_in
, 0, SEEK_SET
)==-1) {
1755 debuga(_("Failed to rewind the excluded codes file %s: %s\n"),ExcludeCodes
,strerror(errno
));
1760 if((excludecode
=(char *) malloc(MemSize
))==NULL
) {
1761 debuga(_("malloc error (%ld bytes required)\n"),MemSize
);
1764 memset(excludecode
,0,MemSize
);
1767 while(fgets(data
,sizeof(data
),fp_in
)!=NULL
) {
1768 if (data
[0]=='#') continue;
1769 for (i
=strlen(data
)-1 ; i
>=0 && (unsigned char)data
[i
]<=' ' ; i
--) data
[i
]='\0';
1771 if (Stored
+i
+2>=MemSize
) {
1772 debuga(_("Too many codes to exclude in file %s\n"),ExcludeCodes
);
1775 strcat(excludecode
,data
);
1776 strcat(excludecode
,";");
1784 void free_excludecodes(void)
1792 int vercode(const char *code
)
1797 if (excludecode
&& excludecode
[0]!='\0') {
1801 if (strncmp(code
,cod
,clen
)==0 && cod
[clen
]==';')
1803 cod
=strchr(cod
,';');
1810 void fixnone(char *str
)
1814 for (i
=strlen(str
)-1 ; i
>=0 && (unsigned char)str
[i
]<=' ' ; i
--);
1815 if(i
==3 && strncmp(str
,"none",4) == 0)
1821 void fixendofline(char *str
)
1825 for (i
=strlen(str
)-1 ; i
>=0 && (unsigned char)str
[i
]<=' ' ; i
--) str
[i
]=0;
1828 #ifdef LEGACY_TESTVALIDUSERCHAR
1829 int testvaliduserchar(const char *user
)
1834 for (y
=0; y
<strlen(UserInvalidChar
); y
++) {
1835 for (x
=0; x
<strlen(user
); x
++) {
1836 if(user
[x
] == UserInvalidChar
[y
])
1843 int testvaliduserchar(const char *user
)
1845 char * p_UserInvalidChar
= UserInvalidChar
;
1846 const char * p_user
;
1848 while( *p_UserInvalidChar
) {
1851 if( *p_UserInvalidChar
== *p_user
)
1855 p_UserInvalidChar
++ ;
1861 int compar( const void *a
, const void *b
)
1863 if( *(int *)a
> *(int *)b
) return 1;
1864 if( *(int *)a
< *(int *)b
) return -1;
1868 int getnumlist( char *buf
, numlist
*list
, const int len
, const int maxvalue
)
1870 int i
, j
, d
, flag
, r1
, r2
;
1871 char *pbuf
, **bp
, *strbufs
[ 24 ];
1874 strtok( buf
, " \t" );
1875 for( *bp
= strtok( NULL
, "," ), list
->len
= 0; *bp
; *bp
= strtok( NULL
, "," ) ) {
1876 if( ++bp
>= &strbufs
[ 24 ] )
1883 for( i
= 0; i
< list
->len
; i
++ ) {
1884 if( strchr( strbufs
[ i
], '-' ) != 0 ) {
1885 pbuf
= strbufs
[ i
];
1886 strtok( pbuf
, "-" );
1887 pbuf
= strtok( NULL
, "\0" );
1888 r1
= atoi( strbufs
[ i
] );
1889 if( ( r2
= atoi( pbuf
) ) >= maxvalue
|| r1
>= r2
)
1891 if( i
+ d
+ ( r2
- r1
) + 1 <= len
) {
1892 for( j
= r1
; j
<= r2
; j
++ )
1893 list
->list
[ i
+ d
++ ] = j
;
1898 if( ( list
->list
[ i
+ d
] = atoi( strbufs
[ i
] ) ) >= maxvalue
)
1902 qsort( list
->list
, list
->len
, sizeof( int ), compar
);
1905 for( i
= 0; i
< list
->len
- 1; i
++ )
1906 if( list
->list
[ i
] == list
->list
[ i
+ 1 ] ) {
1907 for( j
= i
+ 1; j
< list
->len
; j
++ )
1908 list
->list
[ j
- 1 ] = list
->list
[ j
];
1917 void show_info(FILE *fp_ou
)
1921 if(!ShowSargInfo
) return;
1922 zdate(ftime
, sizeof(ftime
), df
);
1923 fputs("<div class=\"info\">",fp_ou
);
1924 fprintf(fp_ou
,_("Generated by <a href='%s'>%s-%s</a> on %s"),URL
,PGM
,VERSION
,ftime
);
1925 fputs("</div>\n",fp_ou
);
1928 void show_sarg(FILE *fp_ou
, int depth
)
1932 if(!ShowSargLogo
) return;
1933 fputs("<div class=\"logo\"><a href=\"http://sarg.sourceforge.net\"><img src=\"",fp_ou
);
1934 for (i
=0 ; i
<depth
; i
++)
1936 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
);
1939 void write_logo_image(FILE *fp_ou
)
1941 if(LogoImage
[0]!='\0')
1942 fprintf(fp_ou
, "<div class=\"logo\"><img src=\"%s\" width=\"%s\" height=\"%s\" alt=\"Logo\"> %s</div>\n",LogoImage
,Width
,Height
,LogoText
);
1945 void write_html_head(FILE *fp_ou
, int depth
, const char *page_title
,int javascript
)
1949 fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n",fp_ou
);
1950 fprintf(fp_ou
, "<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet
);
1951 if (page_title
) fprintf(fp_ou
,"<title>%s</title>\n",page_title
);
1953 if ((javascript
& HTML_JS_SORTTABLE
)!=0 && SortTableJs
[0]) {
1954 fputs("<script type=\"text/javascript\" src=\"",fp_ou
);
1955 if (strncmp(SortTableJs
,"../",3)==0) {
1956 for (i
=0 ; i
<depth
; i
++) fputs("../",fp_ou
);
1958 fputs(SortTableJs
,fp_ou
);
1959 fputs("\"></script>\n",fp_ou
);
1961 fputs("</head>\n<body>\n",fp_ou
);
1964 void write_html_header(FILE *fp_ou
, int depth
, const char *page_title
,int javascript
)
1966 write_html_head(fp_ou
,depth
,page_title
,javascript
);
1967 write_logo_image(fp_ou
);
1968 show_sarg(fp_ou
, depth
);
1969 fprintf(fp_ou
,"<div class=\"title\"><table cellpadding=\"0\" cellspacing=\"0\">\n<tr><th class=\"title_c\">%s</th></tr>\n",Title
);
1972 void close_html_header(FILE *fp_ou
)
1974 fputs("</table></div>\n",fp_ou
);
1977 void write_html_trailer(FILE *fp_ou
)
1980 fputs("</body>\n</html>\n",fp_ou
);
1983 void output_html_string(FILE *fp_ou
,const char *str
,int maxlen
)
1987 while (*str
&& (maxlen
<=0 || i
<maxlen
)) {
1990 fputs("&",fp_ou
);
1993 fputs("<",fp_ou
);
1996 fputs(">",fp_ou
);
1999 fputs(""",fp_ou
);
2002 fputs("'",fp_ou
);
2010 if (maxlen
>0 && i
>=maxlen
)
2011 fputs("…",fp_ou
);
2014 void output_html_url(FILE *fp_ou
,const char *url
)
2018 fputs("&",fp_ou
);
2026 Write a host name inside an A tag of a HTML file. If the host name starts
2027 with a star, it is assumed to be an alias that cannot be put inside a link
2028 so the A tag is not written around the host name.
2030 \param fp_ou The handle of the HTML file.
2031 \param url The host to display in the HTML file.
2032 \param maxlen The maximum number of characters to print into the host name.
2034 void output_html_link(FILE *fp_ou
,const char *url
,int maxlen
)
2036 if (url
[0]==ALIAS_PREFIX
) {
2037 // this is an alias, no need for a A tag
2038 output_html_string(fp_ou
,url
+1,100);
2040 if (skip_scheme(url
)==url
)
2041 fputs("<a href=\"http://",fp_ou
);//no scheme in the url, assume http:// to make the link clickable
2043 fputs("<a href=\"",fp_ou
);//the scheme is in the url, no need to add one
2044 output_html_url(fp_ou
,url
);
2046 output_html_string(fp_ou
,url
,100);
2047 fputs("</a>",fp_ou
);
2051 void url_module(const char *url
, char *w2
)
2057 for(x
=strlen(url
)-1; x
>=0; x
--) {
2058 if(url
[x
] == '/' || y
>=sizeof(w
)-1) break;
2067 for(y
=y
-1; y
>=0; y
--) {
2074 Mangle an URL to produce a part that can be used as an anchor in
2075 a html <a name=""> tag.
2077 \param url The URL to mangle.
2078 \param anchor The buffer to write the mangled URL.
2079 \param size The size of the buffer.
2081 void url_to_anchor(const char *url
,char *anchor
,int size
)
2087 for (i
=0 ; url
[i
] && url
[i
]!='/' && url
[i
]!='?' ; i
++);
2094 // only keep really safe characters
2100 if(isalnum(url
[i
]) || url
[i
]=='-' || url
[i
]=='_' || url
[i
]=='.') {
2104 if (!skip
) anchor
[--j
]='_';
2122 printf(_("SARG Version: %s\n"),VERSION
);
2123 #if defined(ENABLE_NLS) && defined(HAVE_LOCALE_H)
2125 printf(_("\nFor the translation to work, a valid message file should be installed as "
2126 "\"%s/<Locale>/LC_MESSAGES/%s.mo\" where <Locale> is derived from the effective locale.\n"),LOCALEDIR
,PACKAGE_NAME
);
2127 if (CurrentLocale
) {
2128 printf(_("Currently effective locale is \"%s\".\n"),CurrentLocale
);
2130 printf(_("Locale is not set in the environment variable.\n"));
2132 // TRANSLATORS: You may change this message to tell the reader that the language is correctly supported.
2133 printf(_("If this message is in English, then your language is not supported or not correctly installed.\n"));
2139 char *get_param_value(const char *param
,char *line
)
2143 while (*line
==' ' || *line
=='\t') line
++;
2145 if (strncasecmp(line
,param
,plen
)) return(NULL
);
2146 if (line
[plen
]!=' ' && line
[plen
]!='\t') return(NULL
);
2148 while (*line
==' ' || *line
=='\t') line
++;
2152 void unlinkdir(const char *dir
,bool contentonly
)
2156 struct dirent
*direntp
;
2162 while ((direntp
= readdir(dirp
)) != NULL
) {
2163 if (direntp
->d_name
[0] == '.' && (direntp
->d_name
[1] == '\0' ||
2164 (direntp
->d_name
[1] == '.' && direntp
->d_name
[2] == '\0')))
2166 if (snprintf(dname
,sizeof(dname
),"%s/%s",dir
,direntp
->d_name
)>=sizeof(dname
)) {
2167 debuga(_("Path too long: "));
2168 debuga_more("%s/%s\n",dir
,direntp
->d_name
);
2172 err
=lstat(dname
,&st
);
2174 err
=stat(dname
,&st
);
2177 debuga(_("Cannot stat \"%s\": %s\n"),dname
,strerror(errno
));
2180 if (S_ISREG(st
.st_mode
)) {
2181 if (unlink(dname
)) {
2182 debuga(_("Cannot delete \"%s\": %s\n"),dname
,strerror(errno
));
2185 } else if (S_ISDIR(st
.st_mode
)) {
2188 debuga(_("Unknown path type %s\n"),dname
);
2195 debuga(_("Cannot delete \"%s\": %s\n"),dir
,strerror(errno
));
2202 Delete every file from the temporary directory where sarg is told to store its
2205 As any stray file left over by a previous run would be included in the report, we
2206 must delete every file from the temporary directory before we start processing the logs.
2208 But the temporary directory is given by the user either in the configuration file or
2209 on the command line. We check that the user didn't give a wrong directory by looking
2210 at the files stored in the directory. If a single file is not one of ours, we abort.
2212 \param dir The temporary directory to purge.
2214 void emptytmpdir(const char *dir
)
2218 struct dirent
*direntp
;
2224 static const char *TmpExt
[]=
2241 // make sure the temporary directory contains only our files
2242 while ((direntp
= readdir(dirp
)) != NULL
) {
2243 if (direntp
->d_name
[0] == '.' && (direntp
->d_name
[1] == '\0' ||
2244 (direntp
->d_name
[1] == '.' && direntp
->d_name
[2] == '\0')))
2247 // is it one of our files
2248 dlen
=strlen(direntp
->d_name
);
2249 for (i
=sizeof(TmpExt
)/sizeof(TmpExt
[0])-1 ; i
>=0 ; i
--) {
2250 elen
=strlen(TmpExt
[i
]);
2251 if (dlen
>=elen
&& strcasecmp(direntp
->d_name
+dlen
-elen
,TmpExt
[i
])==0) break;
2254 debuga(_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2255 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2256 "directory or manually delete the content of \"%s\"\n"),direntp
->d_name
,dir
,dir
);
2260 if (snprintf(dname
,sizeof(dname
),"%s/%s",dir
,direntp
->d_name
)>=sizeof(dname
)) {
2261 debuga(_("Path too long: "));
2262 debuga_more("%s/%s\n",dir
,direntp
->d_name
);
2267 err
=lstat(dname
,&st
);
2269 err
=stat(dname
,&st
);
2272 debuga(_("Cannot stat \"%s\": %s\n"),dname
,strerror(errno
));
2275 if (S_ISDIR(st
.st_mode
)) {
2277 } else if (!S_ISREG(st
.st_mode
)) {
2278 debuga(_("Unknown path type for \"%s\". Check temporary directory\n"),dname
);
2284 // now delete our files
2285 while ((direntp
= readdir(dirp
)) != NULL
) {
2286 if (direntp
->d_name
[0] == '.' && (direntp
->d_name
[1] == '\0' ||
2287 (direntp
->d_name
[1] == '.' && direntp
->d_name
[2] == '\0')))
2290 // is it one of our files
2291 dlen
=strlen(direntp
->d_name
);
2292 for (i
=sizeof(TmpExt
)/sizeof(TmpExt
[0])-1 ; i
>=0 ; i
--) {
2293 elen
=strlen(TmpExt
[i
]);
2294 if (dlen
>=elen
&& strcasecmp(direntp
->d_name
+dlen
-elen
,TmpExt
[i
])==0) break;
2297 debuga(_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2298 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2299 "directory or manually delete the content of \"%s\"\n"),direntp
->d_name
,dir
,dir
);
2303 if (snprintf(dname
,sizeof(dname
),"%s/%s",dir
,direntp
->d_name
)>=sizeof(dname
)) {
2304 debuga(_("Path too long: "));
2305 debuga_more("%s/%s\n",dir
,direntp
->d_name
);
2309 err
=lstat(dname
,&st
);
2311 err
=stat(dname
,&st
);
2314 debuga(_("Cannot stat \"%s\": %s\n"),dname
,strerror(errno
));
2317 if (S_ISREG(st
.st_mode
)) {
2318 if (unlink(dname
)) {
2319 debuga(_("Cannot delete \"%s\": %s\n"),dname
,strerror(errno
));
2323 debuga(_("Unknown path type %s\n"),dname
);
2330 Extract an url, IPv4 or IPv6 from a buffer. The IP addresses may end with a
2333 \param buf The buffer to parse.
2334 \param text A pointer to set to the beginning of the string pattern. No terminating zero is inserted.
2335 The pointer may be NULL.
2336 \param ipv4 A 4 bytes buffer to store the bytes of the IPv4 address.
2337 \param ipv6 A 8 short integers buffer to store the values of the IPv6 address.
2338 \param nbits The number of prefix bits for an IP address.
2339 \param next The content of the line after the extracted address.
2341 \retval 3 The pattern is a IPv6 address.
2342 \retval 2 The pattern is a IPv4 address.
2343 \retval 1 The patter is a string.
2344 \retval 0 Empty pattern.
2346 int extract_address_mask(const char *buf
,const char **text
,unsigned char *ipv4
,unsigned short int *ipv6
,int *nbits
,const char **next
)
2351 unsigned int value4
, value6
;
2352 unsigned short int addr
[8];
2362 // skip leading spaces and tabs
2363 while (*buf
&& (*buf
==' ' || *buf
=='\t')) buf
++;
2365 // find out the nature of the pattern
2366 ip_size
=0x60 | 0x04;
2377 for (i
=0 ; (unsigned char)buf
[i
]>' ' && buf
[i
]!='/' && buf
[i
]!='?' && (!bracket
|| buf
[i
]!=']') && ip_size
; i
++) {
2378 if (ip_size
& 0x04) {
2379 if (isdigit(buf
[i
])) {
2381 port_num
=port_num
*10+(buf
[i
]-'0');
2382 if (port_num
>65535) ip_size
&=~0x04;
2384 value4
=value4
*10+(buf
[i
]-'0');
2385 if (value4
>0xFFU
) ip_size
&=~0x04;
2387 } else if (buf
[i
]=='.' && addr_len
<4) {
2388 addr
[addr_len
++]=(unsigned short)(value4
& 0xFFU
);
2390 } else if (!port
&& buf
[i
]==':') {
2396 if (ip_size
& 0x60) {
2397 if (isdigit(buf
[i
])) {
2398 value6
=(value6
<<4)+(buf
[i
]-'0');
2400 if (value6
>0xFFFFU
) ip_size
&=~0x60;
2401 } else if (toupper(buf
[i
])>='A' && toupper(buf
[i
])<='F') {
2402 value6
=(value6
<<4)+(toupper(buf
[i
])-'A'+10);
2404 if (value6
>0xFFFFU
) ip_size
&=~0x60;
2405 } else if (buf
[i
]==':' && addr_len
<8) {
2406 if (nibble6_len
>0) {
2407 addr
[addr_len
++]=(unsigned short)(value6
& 0xFFFFU
);
2411 if (buf
[i
+1]==':') {
2420 if (i
==0) return(0);
2421 if (ip_size
& 0x04) {
2425 addr
[addr_len
++]=(unsigned short)(value4
& 0xFFU
);
2427 if (ip_size
& 0x60) {
2428 if (pad_pos
<0 && addr_len
!=7) {
2430 } else if (pad_pos
>=0 && addr_len
>=7)
2432 else if (nibble6_len
>0)
2433 addr
[addr_len
++]=(unsigned short)(value6
& 0xFFFFU
);
2438 if (bracket
) (*text
)--;
2440 while ((unsigned char)buf
[i
]>' ') i
++;
2441 if (next
) *next
=buf
+i
;
2444 max_mask
=(ip_size
& 0x04) ? 4*8 : 8*16;
2448 while (isdigit(buf
[i
])) i
++;
2449 if (mask
<0 || mask
>max_mask
) mask
=max_mask
;
2452 if (ip_size
& 0x60 && bracket
&& buf
[i
]==']') i
++;
2453 if (next
) *next
=buf
+i
;
2454 if (ip_size
& 0x04) {
2455 if (nbits
) *nbits
=mask
;
2456 for (i
=0 ; i
<addr_len
; i
++)
2457 ipv4
[i
]=(unsigned char)addr
[i
];
2462 if (nbits
) *nbits
=mask
;
2467 ipv6
[j
++]=(unsigned short int)addr
[i
++];
2469 while (j
<pad_pos
+pad_len
)
2473 ipv6
[j
++]=(unsigned short int)addr
[i
++];