]>
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(HAVE_BACKTRACE)
34 #define USE_GETWORD_BACKTRACE 1
36 #define USE_GETWORD_BACKTRACE 0
39 static char mtab1
[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
41 //! The list of the HTTP codes to exclude from the report.
42 static char *excludecode
=NULL
;
44 #if USE_GETWORD_BACKTRACE
45 static void getword_backtrace(void)
51 n
=backtrace(buffer
,sizeof(buffer
)/sizeof(buffer
[0]));
53 calls
=backtrace_symbols(buffer
,n
);
55 debuga(_("getword backtrace:\n"));
56 for (i
=0 ; i
<n
; i
++) {
57 fprintf(stderr
,"SARG: %d:%s\n",i
+1,calls
[i
]);
62 #endif //USE_GETWORD_BACKTRACE
64 void getword_start(struct getwordstruct
*gwarea
, const char *line
)
66 gwarea
->beginning
=line
;
71 void getword_restart(struct getwordstruct
*gwarea
)
73 if (gwarea
->modified
) {
74 debuga(_("Cannot parse again the line as it was modified\n"));
77 gwarea
->current
=gwarea
->beginning
;
80 int getword(char *word
, int limit
, struct getwordstruct
*gwarea
, char stop
)
84 for(x
=0;((gwarea
->current
[x
]) && (gwarea
->current
[x
] != stop
));x
++) {
86 debuga(_("End of word not found in getword after %d bytes.\n"),x
);
87 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
88 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
89 debuga(_("searching for \'x%x\'\n"),stop
);
90 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
91 word
[(limit
>0) ? limit
-1 : 0]='\0';
92 #if USE_GETWORD_BACKTRACE
97 word
[x
] = gwarea
->current
[x
];
101 if (gwarea
->current
[x
]) ++x
;
106 int getword_limit(char *word
, int limit
, struct getwordstruct
*gwarea
, char stop
)
111 for(x
=0; x
<limit
&& gwarea
->current
[x
] && gwarea
->current
[x
] != stop
;x
++) {
112 word
[x
] = gwarea
->current
[x
];
116 while (*gwarea
->current
&& *gwarea
->current
!= stop
) gwarea
->current
++;
117 if (*gwarea
->current
) ++gwarea
->current
;
121 int getword_multisep(char *word
, int limit
, struct getwordstruct
*gwarea
, char stop
)
125 for(x
=0;((gwarea
->current
[x
]) && (gwarea
->current
[x
] != stop
));x
++) {
127 debuga(_("End of word not found in getword_multisep after %d bytes.\n"),x
);
128 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
129 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
130 debuga(_("searching for \'x%x\'\n"),stop
);
131 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
132 if (limit
>0) word
[limit
-1]='\0';
133 #if USE_GETWORD_BACKTRACE
136 //exit(EXIT_FAILURE);
139 word
[x
] = gwarea
->current
[x
];
143 while (gwarea
->current
[x
] && gwarea
->current
[x
]==stop
) ++x
;
148 int getword_skip(int limit
, struct getwordstruct
*gwarea
, char stop
)
152 for(x
=0;(gwarea
->current
[x
] && (gwarea
->current
[x
] != stop
));x
++) {
154 debuga(_("End of word not found in getword_skip after %d bytes.\n"),x
);
155 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
156 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
157 debuga(_("searching for \'x%x\'\n"),stop
);
158 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
159 #if USE_GETWORD_BACKTRACE
166 if (gwarea
->current
[x
]) ++x
;
171 int getword_atoll(long long int *number
, struct getwordstruct
*gwarea
, char stop
)
177 if (gwarea
->current
[0] == '-') {
180 } else if (gwarea
->current
[0] == '+') {
184 for(x
=0;isdigit(gwarea
->current
[x
]);x
++) {
185 digit
=gwarea
->current
[x
]-'0';
186 if (*number
>= (LLONG_MAX
-digit
)/10) {
187 debuga(_("Integer overflow detected in getword_atoll in line %s\n"),gwarea
->beginning
);
190 *number
=(*number
* 10) + digit
;
192 if(gwarea
->current
[x
] && gwarea
->current
[x
]!=stop
) {
193 debuga(_("End of number not found in getword_atoll after %d bytes.\n"),x
);
194 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
195 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
196 debuga(_("searching for \'x%x\'\n"),stop
);
197 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
198 #if USE_GETWORD_BACKTRACE
205 if (gwarea
->current
[x
]) ++x
;
210 int getword_atoi(int *number
, struct getwordstruct
*gwarea
, char stop
)
216 if (gwarea
->current
[0] == '-') {
219 } else if (gwarea
->current
[0] == '+') {
223 for(x
=0;isdigit(gwarea
->current
[x
]);x
++) {
224 digit
=gwarea
->current
[x
]-'0';
225 if (*number
> (INT_MAX
-digit
)/10) {
226 debuga(_("Integer overflow detected in getword_atoi in line %s\n"),gwarea
->beginning
);
229 *number
=(*number
* 10) + digit
;
231 if(gwarea
->current
[x
] && gwarea
->current
[x
]!=stop
) {
232 debuga(_("End of number not found in getword_atoi after %d bytes.\n"),x
);
233 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
234 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
235 debuga(_("searching for \'x%x\'\n"),stop
);
236 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
237 #if USE_GETWORD_BACKTRACE
244 if (gwarea
->current
[x
]) ++x
;
249 int getword_atol(long int *number
, struct getwordstruct
*gwarea
, char stop
)
255 if (gwarea
->current
[0] == '-') {
258 } else if (gwarea
->current
[0] == '+') {
262 for(x
=0;isdigit(gwarea
->current
[x
]);x
++) {
263 digit
=gwarea
->current
[x
]-'0';
264 if (*number
> (LONG_MAX
-digit
)/10) {
265 debuga(_("Integer overflow detected in getword_atol in line %s\n"),gwarea
->beginning
);
268 *number
=(*number
* 10) + digit
;
270 if(gwarea
->current
[x
] && gwarea
->current
[x
]!=stop
) {
271 debuga(_("End of number not found in getword_atol after %ld bytes.\n"),x
);
272 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
273 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
274 debuga(_("searching for \'x%x\'\n"),stop
);
275 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
276 #if USE_GETWORD_BACKTRACE
283 if (gwarea
->current
[x
]) ++x
;
288 int getword_atolu(unsigned long int *number
, struct getwordstruct
*gwarea
, char stop
)
293 if (gwarea
->current
[0] == '-') {
294 debuga(_("getword_atolu got a negative number.\n"));
295 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
296 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
299 if (gwarea
->current
[0] == '+') {
303 for(x
=0;isdigit(gwarea
->current
[x
]);x
++) {
304 digit
=gwarea
->current
[x
]-'0';
305 if (*number
> (ULONG_MAX
-digit
)/10) {
306 debuga(_("Integer overflow detected in getword_atolu in line %s\n"),gwarea
->beginning
);
309 *number
=(*number
* 10) + digit
;
311 if(gwarea
->current
[x
] && gwarea
->current
[x
]!=stop
) {
312 debuga(_("End of number not found in getword_atolu after %ld bytes.\n"),x
);
313 debuga(_("Line=\"%s\"\n"),gwarea
->beginning
);
314 debuga(_("Record=\"%s\"\n"),gwarea
->current
);
315 debuga(_("searching for \'x%x\'\n"),stop
);
316 //debuga(_("Maybe you have a broken record or garbage in your access.log file.\n"));
317 #if USE_GETWORD_BACKTRACE
323 if (gwarea
->current
[x
]) ++x
;
329 int getword_ptr(char *orig_line
,char **word
, struct getwordstruct
*gwarea
, char stop
)
332 \note Why pass the original buffer to the function ? Because we must modify it to
333 insert the terminating ASCII zero for the word we return and that's not compatible
334 with getword_restart(). Moreover, getword_start() sometime works on constant strings
335 so this function require the original buffer to detect any missuse.
341 if (orig_line
&& orig_line
!=gwarea
->beginning
) {
342 debuga(_("Invalid buffer passed to getword_ptr\n"));
346 start
=(gwarea
->current
-gwarea
->beginning
);
347 if (word
&& orig_line
) *word
=orig_line
+start
;
348 for(x
=0;((gwarea
->current
[x
]) && (gwarea
->current
[x
] != stop
));x
++);
349 sep
=(gwarea
->current
[x
]!='\0');
350 if (word
&& orig_line
) orig_line
[start
+x
] = '\0';
357 #define MAXLLL 30 //!< Maximum number of digits in long long (a guess).
358 long long int my_atoll (const char *nptr
)
360 long long int returnval
=0LL;
361 int max_digits
= MAXLLL
;
363 // Soak up all the white space
364 while (isspace( *nptr
)) {
368 //For each character left to right
369 //change the character to a single digit
370 //multiply what we had before by 10 and add the new digit
372 while (--max_digits
&& isdigit( *nptr
))
374 returnval
= ( returnval
* 10 ) + ( *nptr
++ - '0' ) ;
380 int is_absolute(const char *path
)
382 if (*path
=='/') return(1);
384 if (isalpha(path
[0]) && path
[1]==':') return(1);
389 void my_mkdir(const char *name
)
395 if(!is_absolute(name
)) {
396 debuga(_("Invalid path (%s). Please, use absolute paths only.\n"),name
);
401 for (i
=0 ; name
[i
] ; i
++) {
403 debuga(_("directory name too long: %s\n"),name
);
406 if (chars
>0 && name
[i
] == '/') {
408 if (access(w0
, R_OK
) != 0) {
409 if (mkdir(w0
,0755)) {
410 debuga(_("Cannot create directory %s - %s\n"),w0
,strerror(errno
));
415 if (name
[i
] != '/') chars
++;
419 if (access(name
, R_OK
) != 0) {
420 if (mkdir(name
,0755)) {
421 debuga(_("Cannot create directory %s - %s\n"),name
,strerror(errno
));
428 void my_lltoa(unsigned long long int n
, char *s
, int ssize
, int len
)
437 debuga(_("The requested number of digits passed to my_lltoa (%d) is bigger than the output buffer size (%d)\n"),len
,ssize
);
442 s
[slen
++] = (n
% 10) + '0';
443 } while ((n
/= 10) > 0 && slen
<ssize
);
446 for (i
= 0, j
= slen
-1; i
<j
; i
++, j
--) {
454 for(j
=slen
; j
>=0; j
--)
461 int month2num(const char *month
)
465 for(m
=0 ; m
<12 && strcmp(mtab1
[m
],month
) != 0; m
++);
469 int builddia(int day
, int month
, int year
)
471 return(year
*10000+month
*100+day
);
477 \param date1 The first date to compare.
478 \param date2 The second date to compare.
480 \retval -1 If date1<date2.
481 \retval 0 If date1==date2.
482 \retval 1 if date1>date2.
484 int compare_date(struct tm
*date1
,struct tm
*date2
)
486 if (date1
->tm_year
<date2
->tm_year
) return(-1);
487 if (date1
->tm_year
>date2
->tm_year
) return(1);
488 if (date1
->tm_mon
<date2
->tm_mon
) return(-1);
489 if (date1
->tm_mon
>date2
->tm_mon
) return(1);
490 if (date1
->tm_mday
<date2
->tm_mday
) return(-1);
491 if (date1
->tm_mday
>date2
->tm_mday
) return(1);
492 if (date1
->tm_hour
<date2
->tm_hour
) return(-1);
493 if (date1
->tm_hour
>date2
->tm_hour
) return(1);
494 if (date1
->tm_min
<date2
->tm_min
) return(-1);
495 if (date1
->tm_min
>date2
->tm_min
) return(1);
496 if (date1
->tm_sec
<date2
->tm_sec
) return(-1);
497 if (date1
->tm_sec
>date2
->tm_sec
) return(1);
501 void buildymd(const char *dia
, const char *mes
, const char *ano
, char *wdata
,int wdata_size
)
506 snprintf(wdata
,wdata_size
,"%04d%02d%02d",atoi(ano
),nmes
+1,atoi(dia
));
510 int conv_month(const char *month
)
514 for(x
=0; x
<12 && strncmp(mtab1
[x
],month
,3)!=0; x
++);
519 const char *conv_month_name(int month
)
523 if (month
<1 || month
>12) {
524 snprintf(str
,sizeof(str
),"%03d",month
);
527 return(mtab1
[month
-1]);
531 Write a debug message to stderr. The message is prefixed by "SARG:" to identify its origin.
533 \param msg The printf like message to format.
534 \param ... The arguments to format in the message.
536 void debuga(const char *msg
,...)
540 fputs(_("SARG: "),stderr
);
542 vfprintf(stderr
,msg
,ap
);
548 Write a debug message to stderr. The message is prefixed by "SARG: (info)".
550 \param msg The printf like message to format.
551 \param ... The arguments to format in the message.
553 void debugaz(const char *msg
,...)
557 fputs(_("SARG: (info) "),stderr
);
559 vfprintf(stderr
,msg
,ap
);
564 char *fixnum(long long int value
, int n
)
566 #define MAXLEN_FIXNUM 256
567 char num
[MAXLEN_FIXNUM
]="";
568 char buf
[MAXLEN_FIXNUM
* 2];
570 static char ret
[MAXLEN_FIXNUM
* 2];
572 register int i
, j
, k
;
574 static char abbrev
[30]="";
576 my_lltoa(value
, num
, sizeof(num
), 0);
578 if(DisplayedValues
==DISPLAY_ABBREV
) {
579 numlen
= strlen(num
);
582 else if (numlen
%3 == 1) {
584 abbrev
[1]=(UseComma
) ? ',' : '.';
589 else if (numlen
%3 == 2) {
592 abbrev
[2]=(UseComma
) ? ',' : '.';
597 else if (numlen
%3 == 0) {
601 abbrev
[3]=(UseComma
) ? ',' : '.';
610 else if (numlen
<= 6)
612 else if (numlen
<= 9)
614 else if (numlen
<= 12)
616 else if (numlen
<= 15)
618 else if (numlen
>= 18)
620 else if (numlen
<= 21)
622 else if (numlen
<= 24)
624 else if (numlen
<= 27)
627 strcat(abbrev
,"???");
632 bzero(buf
, MAXLEN_FIXNUM
*2);
638 for ( i
= strlen(num
) - 1, j
= 0 ; i
> -1; i
--) {
639 if ( k
== 2 && i
!= 0 ) {
642 pbuf
[j
++] = (UseComma
) ? ',' : '.';
652 for ( i
= strlen(pbuf
) - 1, j
= 0 ; i
> -1; i
--, j
++)
661 char *fixnum2(long long int value
, int n
)
663 #define MAXLEN_FIXNUM2 1024
664 char num
[MAXLEN_FIXNUM2
];
665 char buf
[MAXLEN_FIXNUM2
* 2];
667 static char ret
[MAXLEN_FIXNUM2
* 2];
669 register int i
, j
, k
;
671 my_lltoa(value
, num
, sizeof(num
), 0);
672 bzero(buf
, MAXLEN_FIXNUM2
*2);
678 for ( i
= strlen(num
) - 1, j
= 0 ; i
> -1; i
--) {
679 if ( k
== 2 && i
!= 0 ) {
682 pbuf
[j
++] = (UseComma
) ? ',' : '.';
692 for ( i
= strlen(pbuf
) - 1, j
= 0 ; i
> -1; i
--, j
++)
701 char *buildtime(long long int elap
)
703 long int num
= elap
/ 1000LL;
710 min
=(num
% 3600L) / 60L;
712 snprintf(buf
,sizeof(buf
),"%02d:%02d:%02d",hor
,min
,sec
);
719 Get the date stored in the <tt>sarg-date</tt> file of a directory with the connection data.
721 \param dirname The directory to look for the connection directory.
722 \param name The name of the directory whose <tt>sarg-date</tt> file must be read.
723 \param data The buffer to store the content of the file. It must be more than 80
727 \retval -1 File not found.
729 int obtdate(const char *dirname
, const char *name
, char *data
)
734 if (snprintf(wdir
,sizeof(wdir
),"%s%s/sarg-date",dirname
,name
)>=sizeof(wdir
)) {
735 debuga(_("Buffer to small to store %s%s/sarg-date"),dirname
,name
);
738 if ((fp_in
= fopen(wdir
, "rt")) == 0) {
739 if (snprintf(wdir
,sizeof(wdir
),"%s%s/date",dirname
,name
)>=sizeof(wdir
)) {
740 debuga(_("Buffer to small to store %s%s/date"),dirname
,name
);
743 if ((fp_in
= fopen(wdir
, "rt")) == 0) {
749 if (!fgets(data
,80,fp_in
)) {
750 debuga(_("Failed to read the date in %s\n"),wdir
);
760 void formatdate(char *date
,int date_size
,int year
,int month
,int day
,int hour
,int minute
,int second
,int dst
)
766 memset(<m
,0,sizeof(ltm
));
767 if (year
>=1900) ltm
.tm_year
=year
-1900;
768 if (month
>=1 && month
<=12) ltm
.tm_mon
=month
-1;
769 if (day
>=1 && day
<=31) ltm
.tm_mday
=day
;
770 if (hour
>=0 && hour
<24) ltm
.tm_hour
=hour
;
771 if (minute
>=0 && minute
<60) ltm
.tm_min
=minute
;
772 if (second
>=0 && second
<60) ltm
.tm_sec
=second
;
774 unixtime
=mktime(<m
); //fill the missing entries
775 fulltm
=localtime(&unixtime
);
776 //strftime(date,date_size,"%a %b %d %H:%M:%S %Z %Y",fulltm);
777 strftime(date
,date_size
,"%c",fulltm
);
781 void computedate(int year
,int month
,int day
,struct tm
*t
)
783 memset(t
,0,sizeof(*t
));
784 t
->tm_year
=year
-1900;
790 int obtuser(const char *dirname
, const char *name
)
797 if (snprintf(wdir
,sizeof(wdir
),"%s%s/sarg-users",dirname
,name
)>=sizeof(wdir
)) {
798 debuga(_("Buffer too small to store %s%s/sarg-users"),dirname
,name
);
801 if((fp_in
=fopen(wdir
,"r"))==NULL
) {
802 if (snprintf(wdir
,sizeof(wdir
),"%s%s/users",dirname
,name
)>=sizeof(wdir
)) {
803 debuga(_("Buffer too small to store %s%s/users"),dirname
,name
);
806 if((fp_in
=fopen(wdir
,"r"))==NULL
) {
811 if (!fgets(tuser
,sizeof(tuser
),fp_in
)) {
812 debuga(_("Failed to read the number of users in %s\n"),wdir
);
822 void obttotal(const char *dirname
, const char *name
, int nuser
, long long int *tbytes
, long long int *media
)
827 char user
[MAX_USER_LEN
];
829 struct getwordstruct gwarea
;
835 if (snprintf(wdir
,sizeof(wdir
),"%s%s/sarg-general",dirname
,name
)>=sizeof(wdir
)) {
836 debuga(_("Buffer too small to store %s%s/sarg-general"),dirname
,name
);
839 if ((fp_in
= fopen(wdir
, "r")) == 0) {
840 if (snprintf(wdir
,sizeof(wdir
),"%s%s/general",dirname
,name
)>=sizeof(wdir
)) {
841 debuga(_("Buffer too small to store %s%s/general"),dirname
,name
);
844 if ((fp_in
= fopen(wdir
, "r")) == 0) {
849 if ((line
=longline_create())==NULL
) {
850 debuga(_("Not enough memory to read the file %s\n"),wdir
);
854 while((buf
=longline_read(fp_in
,line
))!=NULL
) {
855 if (strncmp(buf
,"TOTAL\t",6) == 0)
857 else if (strncmp(buf
,"TOTAL ",6) == 0)
861 getword_start(&gwarea
,buf
);
862 if (getword(user
,sizeof(user
),&gwarea
,sep
)<0) {
863 debuga(_("There is a invalid user in file %s\n"),wdir
);
866 if(strcmp(user
,"TOTAL") != 0)
868 if (getword_skip(MAXLEN
,&gwarea
,sep
)<0) {
869 debuga(_("There a broken total number of access in file %s\n"),wdir
);
872 if (getword_atoll(tbytes
,&gwarea
,sep
)<0) {
873 debuga(_("There is a broken number of bytes in file %s\n"),wdir
);
879 longline_destroy(&line
);
884 *media
=*tbytes
/ nuser
;
888 int getperiod_fromsarglog(const char *arqtt
,struct periodstruct
*period
)
891 int day0
, month0
, year0
, hour0
, minute0
;
892 int day1
, month1
, year1
, hour1
, minute1
;
895 memset(period
,0,sizeof(*period
));
898 while((str
=strstr(str
,"sarg-"))!=NULL
) {
900 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
901 day0
=(str
[0]-'0')*10+(str
[1]-'0');
902 if (day0
<1 || day0
>31) continue;
904 month0
=(str
[0]-'0')*10+(str
[1]-'0')-1;
905 if (month0
<0 || month0
>11) continue;
908 for (i
=0 ; isdigit(str
[i
]) && i
<4 ; i
++) year0
=year0
*10+(str
[i
]-'0');
911 if (str
[0]!='_') continue;
914 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
915 hour0
=(str
[0]-'0')*10+(str
[1]-'0');
917 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
918 minute0
=(str
[0]-'0')*10+(str
[1]-'0');
921 if (*str
!= '-') continue;
924 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
925 day1
=(str
[0]-'0')*10+(str
[1]-'0');
926 if (day1
<1 || day1
>31) continue;
928 month1
=(str
[0]-'0')*10+(str
[1]-'0')-1;
929 if (month1
<0 || month1
>11) continue;
932 for (i
=0 ; isdigit(str
[i
]) && i
<4 ; i
++) year1
=year1
*10+(str
[i
]-'0');
936 if (str
[0]!='_') continue;
939 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
940 hour1
=(str
[0]-'0')*10+(str
[1]-'0');
942 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
943 minute1
=(str
[0]-'0')*10+(str
[1]-'0');
946 period
->start
.tm_mday
=day0
;
947 period
->start
.tm_mon
=month0
;
948 period
->start
.tm_year
=year0
-1900;
949 period
->start
.tm_hour
=hour0
;
950 period
->start
.tm_min
=minute0
;
951 period
->end
.tm_mday
=day1
;
952 period
->end
.tm_mon
=month1
;
953 period
->end
.tm_year
=year1
-1900;
954 period
->end
.tm_hour
=hour1
;
955 period
->end
.tm_min
=minute1
;
961 void getperiod_fromrange(struct periodstruct
*period
,int dfrom
,int duntil
)
963 memset(&period
->start
,0,sizeof(period
->start
));
964 period
->start
.tm_mday
=dfrom
%100;
965 period
->start
.tm_mon
=(dfrom
/100)%100-1;
966 period
->start
.tm_year
=(dfrom
/10000)-1900;
968 memset(&period
->end
,0,sizeof(period
->end
));
969 period
->end
.tm_mday
=duntil
%100;
970 period
->end
.tm_mon
=(duntil
/100)%100-1;
971 period
->end
.tm_year
=(duntil
/10000)-1900;
975 Update the \a main period to encompass the period in \a candidate.
977 void getperiod_merge(struct periodstruct
*main
,struct periodstruct
*candidate
)
982 mdate
=(main
->start
.tm_year
)*10000+(main
->start
.tm_mon
)*100+main
->start
.tm_mday
;
983 cdate
=(candidate
->start
.tm_year
)*10000+(candidate
->start
.tm_mon
)*100+candidate
->start
.tm_mday
;
984 if (cdate
<mdate
) memcpy(&main
->start
,&candidate
->start
,sizeof(struct tm
));
986 mdate
=(main
->end
.tm_year
)*10000+(main
->end
.tm_mon
)*100+main
->end
.tm_mday
;
987 cdate
=(candidate
->end
.tm_year
)*10000+(candidate
->end
.tm_mon
)*100+candidate
->end
.tm_mday
;
988 if (cdate
>mdate
) memcpy(&main
->end
,&candidate
->end
,sizeof(struct tm
));
991 int getperiod_buildtext(struct periodstruct
*period
)
995 char text1
[40], text2
[40];
998 i
=strftime(text1
, sizeof(text1
), "%Y %b %d", &period
->start
);
1000 i
=strftime(text1
, sizeof(text1
), "%d %b %Y", &period
->start
);
1001 } else /*if (df=='w')*/ {
1002 IndexTree
=INDEX_TREE_FILE
;
1003 i
=strftime(text1
, sizeof(text1
), "%Y.%U", &period
->start
);
1005 if (i
== 0) return(-1);
1007 range
=(period
->start
.tm_year
!=period
->end
.tm_year
||
1008 period
->start
.tm_mon
!=period
->end
.tm_mon
||
1009 period
->start
.tm_mday
!=period
->end
.tm_mday
);
1012 i
=strftime(text2
, sizeof(text2
)-i
, "%Y %b %d", &period
->end
);
1013 } else if (df
=='e') {
1014 i
=strftime(text2
, sizeof(text2
)-i
, "%d %b %Y", &period
->end
);
1016 i
=strftime(text2
, sizeof(text2
)-i
, "%Y.%U", &period
->end
);
1018 if (i
== 0) return(-1);
1022 snprintf(period
->text
,sizeof(period
->text
),"%s-%s",text1
,text2
);
1023 snprintf(period
->html
,sizeof(period
->html
),"%s—%s",text1
,text2
);
1025 safe_strcpy(period
->text
,text1
,sizeof(period
->text
));
1026 safe_strcpy(period
->html
,text1
,sizeof(period
->html
));
1031 static void copy_images(void)
1033 FILE *img_in
, *img_ou
;
1035 char imgdir
[MAXLEN
];
1036 char srcfile
[MAXLEN
];
1037 char dstfile
[MAXLEN
];
1039 struct dirent
*direntp
;
1040 char buffer
[MAXLEN
];
1044 if (snprintf(images
,sizeof(images
),"%simages",outdir
)>=sizeof(images
)) {
1045 debuga(_("Cannot copy images to target directory %simages\n"),outdir
);
1048 if (access(images
,R_OK
)!=0) {
1049 if (mkdir(images
,0755)) {
1050 debuga(_("Cannot create directory %s - %s\n"),images
,strerror(errno
));
1055 strcpy(imgdir
,IMAGEDIR
);
1056 dirp
= opendir(imgdir
);
1058 debuga(_("(util) Can't open directory %s: %s\n"),imgdir
,strerror(errno
));
1061 while ((direntp
= readdir( dirp
)) != NULL
){
1062 if(direntp
->d_name
[0]=='.')
1064 if (snprintf(srcfile
,sizeof(srcfile
),"%s/%s",imgdir
,direntp
->d_name
)>=sizeof(srcfile
)) {
1065 debuga(_("Buffer too small to store %s/%s"),imgdir
,direntp
->d_name
);
1068 if (stat(srcfile
,&info
)) {
1069 debuga(_("Cannot stat \"%s\" - %s\n"),srcfile
,strerror(errno
));
1072 if (S_ISREG(info
.st_mode
)) {
1073 if (snprintf(dstfile
,sizeof(dstfile
),"%s/%s",images
,direntp
->d_name
)>=sizeof(dstfile
)) {
1074 debuga(_("Buffer too small to store %s/%s"),images
,direntp
->d_name
);
1077 img_in
= fopen(srcfile
, "rb");
1079 img_ou
= fopen(dstfile
, "wb");
1081 while ((nread
= fread(buffer
,1,sizeof(buffer
),img_in
))>0) {
1082 if (fwrite(buffer
,1,nread
,img_ou
)!=nread
) {
1083 debuga(_("Failed to copy image %s to %s\n"),srcfile
,dstfile
);
1087 if (fclose(img_ou
)==EOF
) {
1088 debuga(_("Error while copying image %s: %s\n"),dstfile
,strerror(errno
));
1092 fprintf(stderr
,"SARG: (util): %s %s: %s\n", _("Cannot open file")?_("Cannot open file"):"Can't open/create file", dstfile
, strerror(errno
));
1095 fprintf(stderr
,"SARG: (util): %s %s: %s\n", _("Cannot open file")?_("Cannot open file"):"Can't open file", srcfile
, strerror(errno
));
1098 (void) closedir(dirp
);
1104 * Check if the proposed file name conforms to the directory structure layed out
1105 * as a file tree. It is used to check if the file name enumerated while scanning
1106 * a directory content may have been created by sarg running with IndexTree set to
1109 bool IsTreeFileDirName(const char *Name
)
1114 // start year (date format u) or start day (date format e)
1115 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1117 if (isdigit(Name
[2]) && isdigit(Name
[3]))
1119 // date format is either u or w
1123 if (!isdigit(Name
[5]) || !isdigit(Name
[6])) return(false);
1124 return(true);//date format w is confirmed
1131 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1132 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1133 if (i
<0) return(false);
1137 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1142 else if (isalpha(Name
[2]) && isalpha(Name
[3]) && isalpha(Name
[4]))
1148 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1149 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1150 if (i
<0) return(false);
1154 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1162 if (Name
[0]!='-') return(false);
1165 if (DateFormat
=='u')
1167 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1170 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1171 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1172 if (i
<0) return(false);
1175 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1178 else //DateFormat=='e'
1180 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1183 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1184 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1185 if (i
<0) return(false);
1188 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1192 * The directory name may contains additional characters such as a counter if
1193 * a previous report is never overwritten.
1199 * Check if the proposed file name can be the year part of a report tree build with
1200 * IndexTree set to INDEX_TREE_DATE.
1202 bool IsTreeYearFileName(const char *Name
)
1204 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1209 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1212 if (Name
[0]) return(false);
1217 * Check if the proposed file name can be the month part of a report tree build with
1218 * IndexTree set to INDEX_TREE_DATE.
1220 bool IsTreeMonthFileName(const char *Name
)
1224 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1225 m
=(Name
[0]-'0')*10+(Name
[1]-'0');
1226 if (m
<1 || m
>12) return(false);
1231 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1232 m
=(Name
[0]-'0')*10+(Name
[1]-'0');
1233 if (m
<1 || m
>12) return(false);
1236 if (Name
[0]) return(false);
1241 * Check if the proposed file name can be the day part of a report tree build with
1242 * IndexTree set to INDEX_TREE_DATE.
1244 bool IsTreeDayFileName(const char *Name
)
1248 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1249 d
=(Name
[0]-'0')*10+(Name
[1]-'0');
1250 if (d
<1 || d
>31) return(false);
1254 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1255 d
=(Name
[0]-'0')*10+(Name
[1]-'0');
1256 if (d
<1 || d
>31) return(false);
1259 * The directory name may contains additional characters such as a counter if
1260 * a previous report is never overwritten.
1265 int vrfydir(const struct periodstruct
*per1
, const char *addr
, const char *site
, const char *us
, const char *form
)
1270 char dirname2
[MAXLEN
];
1278 strcpy(wdir
,outdir
);
1280 y1
=per1
->start
.tm_year
+1900;
1281 y2
=per1
->end
.tm_year
+1900;
1282 m1
=per1
->start
.tm_mon
+1;
1283 m2
=per1
->end
.tm_mon
+1;
1284 d1
=per1
->start
.tm_mday
;
1285 d2
=per1
->end
.tm_mday
;
1286 if(IndexTree
== INDEX_TREE_DATE
) {
1287 wlen
+=sprintf(wdir
+wlen
,"%04d",y1
);
1288 if(y1
!=y2
) wlen
+=sprintf(wdir
+wlen
,"-%04d",y2
);
1289 if(access(wdir
, R_OK
) != 0)
1292 wlen
+=sprintf(wdir
+wlen
,"/%02d",m1
);
1293 if(m1
!= m2
) wlen
+=sprintf(wdir
+wlen
,"-%02d",m2
);
1294 if(access(wdir
, R_OK
) != 0)
1297 wlen
+=sprintf(wdir
+wlen
,"/%02d",d1
);
1298 if(d1
!=d2
) wlen
+=sprintf(wdir
+wlen
,"-%02d",d2
);
1301 wlen
=snprintf(wdir
+wlen
,sizeof(wdir
)-wlen
,"%04d%s%02d-%04d%s%02d",y1
,
1302 conv_month_name(m1
),d1
,y2
,conv_month_name(m2
),d2
);
1303 } else if (df
== 'e') {
1304 wlen
=snprintf(wdir
+wlen
,sizeof(wdir
)-wlen
,"%02d%s%04d-%02d%s%04d",d1
,
1305 conv_month_name(m1
),y1
,d2
,conv_month_name(m2
),y2
);
1306 } else if (df
== 'w') {
1307 wlen2
=strftime(wdir
+wlen
, sizeof(wdir
)-wlen
, "%Y.%U", &per1
->start
);
1308 if (wlen2
==0) return(-1);
1314 struct userinfostruct
*uinfo
=userinfo_find_from_id(us
);
1317 strcat(wdir
,uinfo
->filename
);
1320 if(addr
[0] != '\0') {
1324 if(site
[0] != '\0') {
1329 strcpy(outdirname
,wdir
);
1331 if(IndexTree
!= INDEX_TREE_DATE
) {
1332 if(!OverwriteReport
) {
1334 if(access(wdir
,R_OK
) == 0) {
1335 sprintf(wdir
,"%s.%d",outdirname
,num
);
1344 debuga(_("File %s already exists, moved to %s\n"),outdirname
,wdir
);
1345 rename(outdirname
,wdir
);
1348 if(access(outdirname
,R_OK
) == 0) {
1349 unlinkdir(outdirname
,1);
1352 my_mkdir(outdirname
);
1354 strcpy(dirname2
,wdir
);
1355 if(!OverwriteReport
) {
1357 if(access(wdir
,R_OK
) == 0) {
1358 sprintf(wdir
,"%s.%d",dirname2
,num
);
1367 debuga(_("File %s already exists, moved to %s\n"),dirname2
,wdir
);
1368 rename(dirname2
,wdir
);
1369 strcpy(dirname2
,wdir
);
1372 if(access(wdir
,R_OK
) == 0) {
1377 if(access(wdir
, R_OK
) != 0)
1381 strcpy(dirname2
,wdir
);
1383 if (snprintf(wdir
,sizeof(wdir
),"%s/sarg-date",outdirname
)>=sizeof(wdir
)) {
1384 debuga(_("Buffer too small to store %s/sarg-date"),outdirname
);
1387 if ((fp_ou
= fopen(wdir
, "wt")) == 0) {
1388 debuga(_("cannot open %s for writing: %s\n"),wdir
,strerror(errno
));
1393 //strftime(wdir,sizeof(wdir),"%a %b %d %H:%M:%S %Z %Y",localtime(&curtime));
1394 loctm
=localtime(&curtime
);
1395 strftime(wdir
,sizeof(wdir
),"%Y-%m-%d %H:%M:%S",loctm
);
1396 if (fprintf(fp_ou
,"%s %d\n",wdir
,loctm
->tm_isdst
)<0) {
1397 debuga(_("Failed to write the date in %s\n"),wdir
);
1401 if (fclose(fp_ou
)==EOF
) {
1402 debuga(_("Failed to write the date in %s: %s\n"),wdir
,strerror(errno
));
1411 Copy a string without overflowing the buffer. The copied string
1412 is properly terminated by an ASCII zero.
1414 \param dest The destination buffer.
1415 \param src The source buffer.
1416 \param length The size of the destination buffer. The program is aborted
1417 if the length is negative or zero.
1419 void safe_strcpy(char *dest
,const char *src
,int length
)
1422 debuga(_("Invalid buffer length passed to the function to safely copy a string\n"));
1425 strncpy(dest
,src
,length
-1);
1426 dest
[length
-1]='\0';
1429 void strip_latin(char *line
)
1436 for (i
=0;line
[i
];i
++){
1438 if (line
[i
]==';') skip
=0;
1450 void zdate(char *ftime
,int ftimesize
, char DateFormat
)
1456 local
= localtime(&t
);
1457 if (DateFormat
=='u')
1458 strftime(ftime
, ftimesize
, "%b/%d/%Y %H:%M", local
);
1459 else if (DateFormat
=='e')
1460 strftime(ftime
, ftimesize
, "%d/%b/%Y-%H:%M", local
);
1461 else if (DateFormat
=='w')
1462 strftime(ftime
, ftimesize
, "%W-%H-%M", local
);
1467 char *fixtime(long long int elap
)
1469 long int num
= elap
/ 1000LL;
1473 static char buf
[20];
1476 min
=(num
% 3600L) / 60L;
1479 if(hor
==0 && min
==0 && sec
==0)
1482 snprintf(buf
,sizeof(buf
),"%d:%02d:%02d",hor
,min
,sec
);
1488 void date_from(char *date
,int date_size
, int *dfrom
, int *duntil
)
1497 if (isdigit(date
[0])) {
1500 if (sscanf(date
,"%d/%d/%d%n",&d0
,&m0
,&y0
,&next
)!=3 || y0
<100 || m0
<1 || m0
>12 || d0
<1 || d0
>31 || next
<0) {
1501 debuga(_("The date passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1504 if (date
[next
]=='-') {
1505 if (sscanf(date
+next
+1,"%d/%d/%d",&d1
,&m1
,&y1
)!=3 || y1
<100 || m1
<1 || m1
>12 || d1
<1 || d1
>31) {
1506 debuga(_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1509 } else if (date
[next
]!='\0') {
1510 debuga(_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1520 struct tm
*Date0
,Date1
;
1522 if (time(&Today
)==(time_t)-1) {
1523 debuga(_("Failed to get the current time\n"));
1526 if (sscanf(date
,"day-%d",&i
)==1) {
1528 debuga(_("Invalid number of days in -d parameter\n"));
1532 Date0
=localtime(&Today
);
1534 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1537 y0
=y1
=Date0
->tm_year
+1900;
1538 m0
=m1
=Date0
->tm_mon
+1;
1539 d0
=d1
=Date0
->tm_mday
;
1540 } else if (sscanf(date
,"week-%d",&i
)==1) {
1542 There is no portable way to find the first day of the week even though the
1543 information is available in the locale. nl_langinfo has the unofficial
1544 parameters _NL_TIME_FIRST_WEEKDAY and _NL_TIME_WEEK_1STDAY but they are
1545 undocumented as is their return value and it is discouraged to use them.
1546 Beside, nl_langinfo isn't available on windows and the first day of the
1547 week isn't available at all on that system.
1549 const int FirstWeekDay
=1;
1553 debuga(_("Invalid number of weeks in -d parameter\n"));
1556 Date0
=localtime(&Today
);
1558 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1561 WeekBegin
=Today
-((Date0
->tm_wday
-FirstWeekDay
+7)%7)*24*60*60;
1562 WeekBegin
-=i
*7*24*60*60;
1563 Date0
=localtime(&WeekBegin
);
1565 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1568 y0
=Date0
->tm_year
+1900;
1571 WeekBegin
+=6*24*60*60;
1572 Date0
=localtime(&WeekBegin
);
1574 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1577 y1
=Date0
->tm_year
+1900;
1580 } else if (sscanf(date
,"month-%d",&i
)==1) {
1582 debuga(_("Invalid number of months in -d parameter\n"));
1585 Date0
=localtime(&Today
);
1587 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1590 if (Date0
->tm_mon
<i
%12) {
1591 y0
=Date0
->tm_year
+1900-i
/12-1;
1592 m0
=(Date0
->tm_mon
+12-i
%12)%12+1;
1595 y0
=Date0
->tm_year
+1900-i
/12;
1596 m0
=Date0
->tm_mon
-i
%12+1;
1599 memcpy(&Date1
,Date0
,sizeof(struct tm
));
1604 Date1
.tm_year
=y0
-1900;
1607 Date1
.tm_year
=y0
-1900+1;
1611 Date0
=localtime(&t1
);
1612 y1
=Date0
->tm_year
+1900;
1616 debuga(_("Invalid date range passed on command line\n"));
1621 *dfrom
=y0
*10000+m0
*100+d0
;
1622 *duntil
=y1
*10000+m1
*100+d1
;
1623 snprintf(date
,date_size
,"%02d/%02d/%04d-%02d/%02d/%04d",d0
,m0
,y0
,d1
,m1
,y1
);
1628 char *strlow(char *string
)
1634 for (s
= string
; *s
; ++s
)
1644 char *strup(char *string
)
1650 for (s
= string
; *s
; ++s
)
1658 void removetmp(const char *outdir
)
1663 if(!RemoveTempFiles
)
1667 debuga(_("Purging temporary file sarg-general\n"));
1669 if (snprintf(filename
,sizeof(filename
),"%s/sarg-general",outdir
)>=sizeof(filename
)) {
1670 debuga(_("(removetmp) directory too long to remove %s/sarg-period\n"),outdir
);
1673 if((fp_gen
=fopen(filename
,"w"))==NULL
){
1674 debuga(_("(removetmp) Cannot open file %s: %s\n"),filename
,strerror(errno
));
1677 totalger(fp_gen
,filename
);
1678 if (fclose(fp_gen
)==EOF
) {
1679 debuga(_("Failed to close %s after writing the total line: %s\n"),filename
,strerror(errno
));
1684 void load_excludecodes(const char *ExcludeCodes
)
1692 if(ExcludeCodes
[0] == '\0')
1695 if((fp_in
=fopen(ExcludeCodes
,"r"))==NULL
) {
1696 debuga(_("(util) Cannot open file %s (exclude_codes): %s\n"),ExcludeCodes
,strerror(errno
));
1700 if (fseek(fp_in
, 0, SEEK_END
)==-1) {
1701 debuga(_("Failed to move till the end of the excluded codes file %s: %s\n"),ExcludeCodes
,strerror(errno
));
1704 MemSize
= ftell(fp_in
);
1706 debuga(_("Cannot get the size of file %s\n"),ExcludeCodes
);
1709 if (fseek(fp_in
, 0, SEEK_SET
)==-1) {
1710 debuga(_("Failed to rewind the excluded codes file %s: %s\n"),ExcludeCodes
,strerror(errno
));
1715 if((excludecode
=(char *) malloc(MemSize
))==NULL
) {
1716 debuga(_("malloc error (%ld)\n"),MemSize
);
1719 memset(excludecode
,0,MemSize
);
1722 while(fgets(data
,sizeof(data
),fp_in
)!=NULL
) {
1723 if (data
[0]=='#') continue;
1724 for (i
=strlen(data
)-1 ; i
>=0 && (unsigned char)data
[i
]<=' ' ; i
--) data
[i
]='\0';
1726 if (Stored
+i
+2>=MemSize
) {
1727 debuga(_("Too many codes to exclude in file %s\n"),ExcludeCodes
);
1730 strcat(excludecode
,data
);
1731 strcat(excludecode
,";");
1739 void free_excludecodes(void)
1747 int vercode(const char *code
)
1752 if (excludecode
&& excludecode
[0]!='\0') {
1756 if (strncmp(code
,cod
,clen
)==0 && cod
[clen
]==';')
1758 cod
=strchr(cod
,';');
1765 void fixnone(char *str
)
1769 for (i
=strlen(str
)-1 ; i
>=0 && (unsigned char)str
[i
]<=' ' ; i
--);
1770 if(i
==3 && strncmp(str
,"none",4) == 0)
1776 void fixendofline(char *str
)
1780 for (i
=strlen(str
)-1 ; i
>=0 && (unsigned char)str
[i
]<=' ' ; i
--) str
[i
]=0;
1783 #ifdef LEGACY_TESTVALIDUSERCHAR
1784 int testvaliduserchar(const char *user
)
1789 for (y
=0; y
<strlen(UserInvalidChar
); y
++) {
1790 for (x
=0; x
<strlen(user
); x
++) {
1791 if(user
[x
] == UserInvalidChar
[y
])
1798 int testvaliduserchar(const char *user
)
1800 char * p_UserInvalidChar
= UserInvalidChar
;
1801 const char * p_user
;
1803 while( *p_UserInvalidChar
) {
1806 if( *p_UserInvalidChar
== *p_user
)
1810 p_UserInvalidChar
++ ;
1816 int compar( const void *a
, const void *b
)
1818 if( *(int *)a
> *(int *)b
) return 1;
1819 if( *(int *)a
< *(int *)b
) return -1;
1823 int getnumlist( char *buf
, numlist
*list
, const int len
, const int maxvalue
)
1825 int i
, j
, d
, flag
, r1
, r2
;
1826 char *pbuf
, **bp
, *strbufs
[ 24 ];
1829 strtok( buf
, " \t" );
1830 for( *bp
= strtok( NULL
, "," ), list
->len
= 0; *bp
; *bp
= strtok( NULL
, "," ) ) {
1831 if( ++bp
>= &strbufs
[ 24 ] )
1838 for( i
= 0; i
< list
->len
; i
++ ) {
1839 if( strchr( strbufs
[ i
], '-' ) != 0 ) {
1840 pbuf
= strbufs
[ i
];
1841 strtok( pbuf
, "-" );
1842 pbuf
= strtok( NULL
, "\0" );
1843 r1
= atoi( strbufs
[ i
] );
1844 if( ( r2
= atoi( pbuf
) ) >= maxvalue
|| r1
>= r2
)
1846 if( i
+ d
+ ( r2
- r1
) + 1 <= len
) {
1847 for( j
= r1
; j
<= r2
; j
++ )
1848 list
->list
[ i
+ d
++ ] = j
;
1853 if( ( list
->list
[ i
+ d
] = atoi( strbufs
[ i
] ) ) >= maxvalue
)
1857 qsort( list
->list
, list
->len
, sizeof( int ), compar
);
1860 for( i
= 0; i
< list
->len
- 1; i
++ )
1861 if( list
->list
[ i
] == list
->list
[ i
+ 1 ] ) {
1862 for( j
= i
+ 1; j
< list
->len
; j
++ )
1863 list
->list
[ j
- 1 ] = list
->list
[ j
];
1872 void show_info(FILE *fp_ou
)
1876 if(!ShowSargInfo
) return;
1877 zdate(ftime
, sizeof(ftime
), df
);
1878 fputs("<div class=\"info\">",fp_ou
);
1879 fprintf(fp_ou
,_("Generated by <a href='%s'>%s-%s</a> on %s"),URL
,PGM
,VERSION
,ftime
);
1880 fputs("</div>\n",fp_ou
);
1883 void show_sarg(FILE *fp_ou
, int depth
)
1887 if(!ShowSargLogo
) return;
1888 fputs("<div class=\"logo\"><a href=\"http://sarg.sourceforge.net\"><img src=\"",fp_ou
);
1889 for (i
=0 ; i
<depth
; i
++)
1891 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
);
1894 void write_logo_image(FILE *fp_ou
)
1896 if(LogoImage
[0]!='\0')
1897 fprintf(fp_ou
, "<div class=\"logo\"><img src=\"%s\" width=\"%s\" height=\"%s\" alt=\"Logo\"> %s</div>\n",LogoImage
,Width
,Height
,LogoText
);
1900 void write_html_head(FILE *fp_ou
, int depth
, const char *page_title
,int javascript
)
1904 fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n",fp_ou
);
1905 fprintf(fp_ou
, "<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet
);
1906 if (page_title
) fprintf(fp_ou
,"<title>%s</title>\n",page_title
);
1908 if ((javascript
& HTML_JS_SORTTABLE
)!=0 && SortTableJs
[0]) {
1909 fputs("<script type=\"text/javascript\" src=\"",fp_ou
);
1910 if (strncmp(SortTableJs
,"../",3)==0) {
1911 for (i
=0 ; i
<depth
; i
++) fputs("../",fp_ou
);
1913 fputs(SortTableJs
,fp_ou
);
1914 fputs("\"></script>\n",fp_ou
);
1916 fputs("</head>\n<body>\n",fp_ou
);
1919 void write_html_header(FILE *fp_ou
, int depth
, const char *page_title
,int javascript
)
1921 write_html_head(fp_ou
,depth
,page_title
,javascript
);
1922 write_logo_image(fp_ou
);
1923 show_sarg(fp_ou
, depth
);
1924 fprintf(fp_ou
,"<div class=\"title\"><table cellpadding=\"0\" cellspacing=\"0\">\n<tr><th class=\"title_c\">%s</th></tr>\n",Title
);
1927 void close_html_header(FILE *fp_ou
)
1929 fputs("</table></div>\n",fp_ou
);
1932 int write_html_trailer(FILE *fp_ou
)
1935 if (fputs("</body>\n</html>\n",fp_ou
)==EOF
) return(-1);
1939 void output_html_string(FILE *fp_ou
,const char *str
,int maxlen
)
1943 while (*str
&& (maxlen
<=0 || i
<maxlen
)) {
1946 fputs("&",fp_ou
);
1949 fputs("<",fp_ou
);
1952 fputs(">",fp_ou
);
1955 fputs(""",fp_ou
);
1958 fputs("'",fp_ou
);
1966 if (maxlen
>0 && i
>=maxlen
)
1967 fputs("…",fp_ou
);
1970 void output_html_url(FILE *fp_ou
,const char *url
)
1974 fputs("&",fp_ou
);
1982 Write a host name inside an A tag of a HTML file. If the host name starts
1983 with a star, it is assumed to be an alias that cannot be put inside a link
1984 so the A tag is not written around the host name.
1986 \param fp_ou The handle of the HTML file.
1987 \param url The host to display in the HTML file.
1988 \param maxlen The maximum number of characters to print into the host name.
1990 void output_html_link(FILE *fp_ou
,const char *url
,int maxlen
)
1992 if (url
[0]==ALIAS_PREFIX
) {
1993 // this is an alias, no need for a A tag
1994 output_html_string(fp_ou
,url
+1,100);
1996 if (skip_scheme(url
)==url
)
1997 fputs("<a href=\"http://",fp_ou
);//no scheme in the url, assume http:// to make the link clickable
1999 fputs("<a href=\"",fp_ou
);//the scheme is in the url, no need to add one
2000 output_html_url(fp_ou
,url
);
2002 output_html_string(fp_ou
,url
,100);
2003 fputs("</a>",fp_ou
);
2007 void url_module(const char *url
, char *w2
)
2013 for(x
=strlen(url
)-1; x
>=0; x
--) {
2014 if(url
[x
] == '/' || y
>=sizeof(w
)-1) break;
2023 for(y
=y
-1; y
>=0; y
--) {
2030 Mangle an URL to produce a part that can be used as an anchor in
2031 a html <a name=""> tag.
2033 \param url The URL to mangle.
2034 \param anchor The buffer to write the mangled URL.
2035 \param size The size of the buffer.
2037 void url_to_anchor(const char *url
,char *anchor
,int size
)
2043 for (i
=0 ; url
[i
] && url
[i
]!='/' && url
[i
]!='?' ; i
++);
2050 // only keep really safe characters
2056 if(isalnum(url
[i
]) || url
[i
]=='-' || url
[i
]=='_' || url
[i
]=='.') {
2060 if (!skip
) anchor
[--j
]='_';
2078 printf(_("SARG Version: %s\n"),VERSION
);
2082 char *get_param_value(const char *param
,char *line
)
2086 while (*line
==' ' || *line
=='\t') line
++;
2088 if (strncasecmp(line
,param
,plen
)) return(NULL
);
2089 if (line
[plen
]!=' ' && line
[plen
]!='\t') return(NULL
);
2091 while (*line
==' ' || *line
=='\t') line
++;
2095 void unlinkdir(const char *dir
,bool contentonly
)
2099 struct dirent
*direntp
;
2105 while ((direntp
= readdir(dirp
)) != NULL
) {
2106 if (direntp
->d_name
[0] == '.' && (direntp
->d_name
[1] == '\0' ||
2107 (direntp
->d_name
[1] == '.' && direntp
->d_name
[2] == '\0')))
2109 if (snprintf(dname
,sizeof(dname
),"%s/%s",dir
,direntp
->d_name
)>=sizeof(dname
)) {
2110 debuga(_("directory name to delete too long: %s/%s\n"),dir
,direntp
->d_name
);
2114 err
=lstat(dname
,&st
);
2116 err
=stat(dname
,&st
);
2119 debuga(_("cannot stat %s\n"),dname
);
2122 if (S_ISREG(st
.st_mode
)) {
2123 if (unlink(dname
)) {
2124 debuga(_("Cannot delete \"%s\": %s\n"),dname
,strerror(errno
));
2127 } else if (S_ISDIR(st
.st_mode
)) {
2130 debuga(_("unknown path type %s\n"),dname
);
2137 debuga(_("Cannot delete \"%s\": %s\n"),dir
,strerror(errno
));
2144 Delete every file from the temporary directory where sarg is told to store its
2147 As any stray file left over by a previous run would be included in the report, we
2148 must delete every file from the temporary directory before we start processing the logs.
2150 But the temporary directory is given by the user either in the configuration file or
2151 on the command line. We check that the user didn't give a wrong directory by looking
2152 at the files stored in the directory. If a single file is not one of ours, we abort.
2154 \param dir The temporary directory to purge.
2156 void emptytmpdir(const char *dir
)
2160 struct dirent
*direntp
;
2166 static const char *TmpExt
[]=
2183 // make sure the temporary directory contains only our files
2184 while ((direntp
= readdir(dirp
)) != NULL
) {
2185 if (direntp
->d_name
[0] == '.' && (direntp
->d_name
[1] == '\0' ||
2186 (direntp
->d_name
[1] == '.' && direntp
->d_name
[2] == '\0')))
2189 // is it one of our files
2190 dlen
=strlen(direntp
->d_name
);
2191 for (i
=sizeof(TmpExt
)/sizeof(TmpExt
[0])-1 ; i
>=0 ; i
--) {
2192 elen
=strlen(TmpExt
[i
]);
2193 if (dlen
>=elen
&& strcasecmp(direntp
->d_name
+dlen
-elen
,TmpExt
[i
])==0) break;
2196 debuga(_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2197 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2198 "directory or manually delete the content of \"%s\"\n"),direntp
->d_name
,dir
,dir
);
2202 if (snprintf(dname
,sizeof(dname
),"%s/%s",dir
,direntp
->d_name
)>=sizeof(dname
)) {
2203 debuga(_("directory name to delete too long: %s/%s\n"),dir
,direntp
->d_name
);
2208 err
=lstat(dname
,&st
);
2210 err
=stat(dname
,&st
);
2213 debuga(_("cannot stat \"%s\"\n"),dname
);
2216 if (S_ISDIR(st
.st_mode
)) {
2218 } else if (!S_ISREG(st
.st_mode
)) {
2219 debuga(_("Unknown path type for \"%s\". Check temporary directory\n"),dname
);
2225 // now delete our files
2226 while ((direntp
= readdir(dirp
)) != NULL
) {
2227 if (direntp
->d_name
[0] == '.' && (direntp
->d_name
[1] == '\0' ||
2228 (direntp
->d_name
[1] == '.' && direntp
->d_name
[2] == '\0')))
2231 // is it one of our files
2232 dlen
=strlen(direntp
->d_name
);
2233 for (i
=sizeof(TmpExt
)/sizeof(TmpExt
[0])-1 ; i
>=0 ; i
--) {
2234 elen
=strlen(TmpExt
[i
]);
2235 if (dlen
>=elen
&& strcasecmp(direntp
->d_name
+dlen
-elen
,TmpExt
[i
])==0) break;
2238 debuga(_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2239 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2240 "directory or manually delete the content of \"%s\"\n"),direntp
->d_name
,dir
,dir
);
2244 if (snprintf(dname
,sizeof(dname
),"%s/%s",dir
,direntp
->d_name
)>=sizeof(dname
)) {
2245 debuga(_("directory name to delete too long: %s/%s\n"),dir
,direntp
->d_name
);
2249 err
=lstat(dname
,&st
);
2251 err
=stat(dname
,&st
);
2254 debuga(_("cannot stat \"%s\"\n"),dname
);
2257 if (S_ISREG(st
.st_mode
)) {
2258 if (unlink(dname
)) {
2259 debuga(_("Cannot delete \"%s\": %s\n"),dname
,strerror(errno
));
2263 debuga(_("unknown path type %s\n"),dname
);
2270 Extract an url, IPv4 or IPv6 from a buffer. The IP addresses may end with a
2273 \param buf The buffer to parse.
2274 \param text A pointer to set to the beginning of the string pattern. No terminating zero is inserted.
2275 The pointer may be NULL.
2276 \param ipv4 A 4 bytes buffer to store the bytes of the IPv4 address.
2277 \param ipv6 A 8 short integers buffer to store the values of the IPv6 address.
2278 \param nbits The number of prefix bits for an IP address.
2279 \param next The content of the line after the extracted address.
2281 \retval 3 The pattern is a IPv6 address.
2282 \retval 2 The pattern is a IPv4 address.
2283 \retval 1 The patter is a string.
2284 \retval 0 Empty pattern.
2286 int extract_address_mask(const char *buf
,const char **text
,unsigned char *ipv4
,unsigned short int *ipv6
,int *nbits
,const char **next
)
2291 unsigned int value4
, value6
;
2292 unsigned short int addr
[8];
2302 // skip leading spaces and tabs
2303 while (*buf
&& (*buf
==' ' || *buf
=='\t')) buf
++;
2305 // find out the nature of the pattern
2306 ip_size
=0x60 | 0x04;
2317 for (i
=0 ; (unsigned char)buf
[i
]>' ' && buf
[i
]!='/' && buf
[i
]!='?' && (!bracket
|| buf
[i
]!=']') && ip_size
; i
++) {
2318 if (ip_size
& 0x04) {
2319 if (isdigit(buf
[i
])) {
2321 port_num
=port_num
*10+(buf
[i
]-'0');
2322 if (port_num
>65535) ip_size
&=~0x04;
2324 value4
=value4
*10+(buf
[i
]-'0');
2325 if (value4
>0xFFU
) ip_size
&=~0x04;
2327 } else if (buf
[i
]=='.' && addr_len
<4) {
2328 addr
[addr_len
++]=(unsigned short)(value4
& 0xFFU
);
2330 } else if (!port
&& buf
[i
]==':') {
2336 if (ip_size
& 0x60) {
2337 if (isdigit(buf
[i
])) {
2338 value6
=(value6
<<4)+(buf
[i
]-'0');
2340 if (value6
>0xFFFFU
) ip_size
&=~0x60;
2341 } else if (toupper(buf
[i
])>='A' && toupper(buf
[i
])<='F') {
2342 value6
=(value6
<<4)+(toupper(buf
[i
])-'A'+10);
2344 if (value6
>0xFFFFU
) ip_size
&=~0x60;
2345 } else if (buf
[i
]==':' && addr_len
<8) {
2346 if (nibble6_len
>0) {
2347 addr
[addr_len
++]=(unsigned short)(value6
& 0xFFFFU
);
2351 if (buf
[i
+1]==':') {
2360 if (i
==0) return(0);
2361 if (ip_size
& 0x04) {
2365 addr
[addr_len
++]=(unsigned short)(value4
& 0xFFU
);
2367 if (ip_size
& 0x60) {
2368 if (pad_pos
<0 && addr_len
!=7) {
2370 } else if (pad_pos
>=0 && addr_len
>=7)
2372 else if (nibble6_len
>0)
2373 addr
[addr_len
++]=(unsigned short)(value6
& 0xFFFFU
);
2378 if (bracket
) (*text
)--;
2380 while ((unsigned char)buf
[i
]>' ') i
++;
2381 if (next
) *next
=buf
+i
;
2384 max_mask
=(ip_size
& 0x04) ? 4*8 : 8*16;
2388 while (isdigit(buf
[i
])) i
++;
2389 if (mask
<0 || mask
>max_mask
) mask
=max_mask
;
2392 if (ip_size
& 0x60 && bracket
&& buf
[i
]==']') i
++;
2393 if (next
) *next
=buf
+i
;
2394 if (ip_size
& 0x04) {
2395 if (nbits
) *nbits
=mask
;
2396 for (i
=0 ; i
<addr_len
; i
++)
2397 ipv4
[i
]=(unsigned char)addr
[i
];
2402 if (nbits
) *nbits
=mask
;
2407 ipv6
[j
++]=(unsigned short int)addr
[i
++];
2409 while (j
<pad_pos
+pad_len
)
2413 ipv6
[j
++]=(unsigned short int)addr
[i
++];