]>
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 void name_month(char *month
,int month_len
)
533 int x
, z
=atoi(month
)-1;
536 struct getwordstruct gwarea
;
538 strcpy(m
,_("January,February,March,April,May,June,July,August,September,October,November,December"));
539 getword_start(&gwarea
,m
);
542 if (getword_multisep(w
,sizeof(w
),&gwarea
,',')<0) {
543 debuga(_("The internal list of month names is invalid. Please report this bug to the translator.\n"));
546 if (getword_multisep(month
,month_len
,&gwarea
,',')<0) {
547 debuga(_("The internal list of month names is invalid. Please report this bug to the translator.\n"));
554 Write a debug message to stderr. The message is prefixed by "SARG:" to identify its origin.
556 \param msg The printf like message to format.
557 \param ... The arguments to format in the message.
559 void debuga(const char *msg
,...)
563 fputs(_("SARG: "),stderr
);
565 vfprintf(stderr
,msg
,ap
);
571 Write a debug message to stderr. The message is prefixed by "SARG: (info)".
573 \param msg The printf like message to format.
574 \param ... The arguments to format in the message.
576 void debugaz(const char *msg
,...)
580 fputs(_("SARG: (info) "),stderr
);
582 vfprintf(stderr
,msg
,ap
);
587 char *fixnum(long long int value
, int n
)
589 #define MAXLEN_FIXNUM 256
590 char num
[MAXLEN_FIXNUM
]="";
591 char buf
[MAXLEN_FIXNUM
* 2];
593 static char ret
[MAXLEN_FIXNUM
* 2];
595 register int i
, j
, k
;
597 static char abbrev
[30]="";
599 my_lltoa(value
, num
, sizeof(num
), 0);
601 if(DisplayedValues
==DISPLAY_ABBREV
) {
602 numlen
= strlen(num
);
605 else if (numlen
%3 == 1) {
607 abbrev
[1]=(UseComma
) ? ',' : '.';
612 else if (numlen
%3 == 2) {
615 abbrev
[2]=(UseComma
) ? ',' : '.';
620 else if (numlen
%3 == 0) {
624 abbrev
[3]=(UseComma
) ? ',' : '.';
633 else if (numlen
<= 6)
635 else if (numlen
<= 9)
637 else if (numlen
<= 12)
639 else if (numlen
<= 15)
641 else if (numlen
>= 18)
643 else if (numlen
<= 21)
645 else if (numlen
<= 24)
647 else if (numlen
<= 27)
650 strcat(abbrev
,"???");
655 bzero(buf
, MAXLEN_FIXNUM
*2);
661 for ( i
= strlen(num
) - 1, j
= 0 ; i
> -1; i
--) {
662 if ( k
== 2 && i
!= 0 ) {
665 pbuf
[j
++] = (UseComma
) ? ',' : '.';
675 for ( i
= strlen(pbuf
) - 1, j
= 0 ; i
> -1; i
--, j
++)
684 char *fixnum2(long long int value
, int n
)
686 #define MAXLEN_FIXNUM2 1024
687 char num
[MAXLEN_FIXNUM2
];
688 char buf
[MAXLEN_FIXNUM2
* 2];
690 static char ret
[MAXLEN_FIXNUM2
* 2];
692 register int i
, j
, k
;
694 my_lltoa(value
, num
, sizeof(num
), 0);
695 bzero(buf
, MAXLEN_FIXNUM2
*2);
701 for ( i
= strlen(num
) - 1, j
= 0 ; i
> -1; i
--) {
702 if ( k
== 2 && i
!= 0 ) {
705 pbuf
[j
++] = (UseComma
) ? ',' : '.';
715 for ( i
= strlen(pbuf
) - 1, j
= 0 ; i
> -1; i
--, j
++)
724 char *buildtime(long long int elap
)
726 long int num
= elap
/ 1000LL;
733 min
=(num
% 3600L) / 60L;
735 snprintf(buf
,sizeof(buf
),"%02d:%02d:%02d",hor
,min
,sec
);
742 Get the date stored in the <tt>sarg-date</tt> file of a directory with the connection data.
744 \param dirname The directory to look for the connection directory.
745 \param name The name of the directory whose <tt>sarg-date</tt> file must be read.
746 \param data The buffer to store the content of the file. It must be more than 80
750 \retval -1 File not found.
752 int obtdate(const char *dirname
, const char *name
, char *data
)
757 if (snprintf(wdir
,sizeof(wdir
),"%s%s/sarg-date",dirname
,name
)>=sizeof(wdir
)) {
758 debuga(_("Buffer to small to store %s%s/sarg-date"),dirname
,name
);
761 if ((fp_in
= fopen(wdir
, "rt")) == 0) {
762 if (snprintf(wdir
,sizeof(wdir
),"%s%s/date",dirname
,name
)>=sizeof(wdir
)) {
763 debuga(_("Buffer to small to store %s%s/date"),dirname
,name
);
766 if ((fp_in
= fopen(wdir
, "rt")) == 0) {
772 if (!fgets(data
,80,fp_in
)) {
773 debuga(_("Failed to read the date in %s\n"),wdir
);
783 void formatdate(char *date
,int date_size
,int year
,int month
,int day
,int hour
,int minute
,int second
,int dst
)
789 memset(<m
,0,sizeof(ltm
));
790 if (year
>=1900) ltm
.tm_year
=year
-1900;
791 if (month
>=1 && month
<=12) ltm
.tm_mon
=month
-1;
792 if (day
>=1 && day
<=31) ltm
.tm_mday
=day
;
793 if (hour
>=0 && hour
<24) ltm
.tm_hour
=hour
;
794 if (minute
>=0 && minute
<60) ltm
.tm_min
=minute
;
795 if (second
>=0 && second
<60) ltm
.tm_sec
=second
;
797 unixtime
=mktime(<m
); //fill the missing entries
798 fulltm
=localtime(&unixtime
);
799 //strftime(date,date_size,"%a %b %d %H:%M:%S %Z %Y",fulltm);
800 strftime(date
,date_size
,"%c",fulltm
);
804 void computedate(int year
,int month
,int day
,struct tm
*t
)
806 memset(t
,0,sizeof(*t
));
807 t
->tm_year
=year
-1900;
813 int obtuser(const char *dirname
, const char *name
)
820 if (snprintf(wdir
,sizeof(wdir
),"%s%s/sarg-users",dirname
,name
)>=sizeof(wdir
)) {
821 debuga(_("Buffer too small to store %s%s/sarg-users"),dirname
,name
);
824 if((fp_in
=fopen(wdir
,"r"))==NULL
) {
825 if (snprintf(wdir
,sizeof(wdir
),"%s%s/users",dirname
,name
)>=sizeof(wdir
)) {
826 debuga(_("Buffer too small to store %s%s/users"),dirname
,name
);
829 if((fp_in
=fopen(wdir
,"r"))==NULL
) {
834 if (!fgets(tuser
,sizeof(tuser
),fp_in
)) {
835 debuga(_("Failed to read the number of users in %s\n"),wdir
);
845 void obttotal(const char *dirname
, const char *name
, int nuser
, long long int *tbytes
, long long int *media
)
850 char user
[MAX_USER_LEN
];
852 struct getwordstruct gwarea
;
858 if (snprintf(wdir
,sizeof(wdir
),"%s%s/sarg-general",dirname
,name
)>=sizeof(wdir
)) {
859 debuga(_("Buffer too small to store %s%s/sarg-general"),dirname
,name
);
862 if ((fp_in
= fopen(wdir
, "r")) == 0) {
863 if (snprintf(wdir
,sizeof(wdir
),"%s%s/general",dirname
,name
)>=sizeof(wdir
)) {
864 debuga(_("Buffer too small to store %s%s/general"),dirname
,name
);
867 if ((fp_in
= fopen(wdir
, "r")) == 0) {
872 if ((line
=longline_create())==NULL
) {
873 debuga(_("Not enough memory to read the file %s\n"),wdir
);
877 while((buf
=longline_read(fp_in
,line
))!=NULL
) {
878 if (strncmp(buf
,"TOTAL\t",6) == 0)
880 else if (strncmp(buf
,"TOTAL ",6) == 0)
884 getword_start(&gwarea
,buf
);
885 if (getword(user
,sizeof(user
),&gwarea
,sep
)<0) {
886 debuga(_("There is a invalid user in file %s\n"),wdir
);
889 if(strcmp(user
,"TOTAL") != 0)
891 if (getword_skip(MAXLEN
,&gwarea
,sep
)<0) {
892 debuga(_("There a broken total number of access in file %s\n"),wdir
);
895 if (getword_atoll(tbytes
,&gwarea
,sep
)<0) {
896 debuga(_("There is a broken number of bytes in file %s\n"),wdir
);
902 longline_destroy(&line
);
907 *media
=*tbytes
/ nuser
;
911 int getperiod_fromsarglog(const char *arqtt
,struct periodstruct
*period
)
914 int day0
, month0
, year0
, hour0
, minute0
;
915 int day1
, month1
, year1
, hour1
, minute1
;
918 memset(period
,0,sizeof(*period
));
921 while((str
=strstr(str
,"sarg-"))!=NULL
) {
923 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
924 day0
=(str
[0]-'0')*10+(str
[1]-'0');
925 if (day0
<1 || day0
>31) continue;
927 month0
=(str
[0]-'0')*10+(str
[1]-'0')-1;
928 if (month0
<0 || month0
>11) continue;
931 for (i
=0 ; isdigit(str
[i
]) && i
<4 ; i
++) year0
=year0
*10+(str
[i
]-'0');
934 if (str
[0]!='_') continue;
937 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
938 hour0
=(str
[0]-'0')*10+(str
[1]-'0');
940 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
941 minute0
=(str
[0]-'0')*10+(str
[1]-'0');
944 if (*str
!= '-') continue;
947 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
948 day1
=(str
[0]-'0')*10+(str
[1]-'0');
949 if (day1
<1 || day1
>31) continue;
951 month1
=(str
[0]-'0')*10+(str
[1]-'0')-1;
952 if (month1
<0 || month1
>11) continue;
955 for (i
=0 ; isdigit(str
[i
]) && i
<4 ; i
++) year1
=year1
*10+(str
[i
]-'0');
959 if (str
[0]!='_') continue;
962 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
963 hour1
=(str
[0]-'0')*10+(str
[1]-'0');
965 if (!isdigit(str
[0]) || !isdigit(str
[1])) continue;
966 minute1
=(str
[0]-'0')*10+(str
[1]-'0');
969 period
->start
.tm_mday
=day0
;
970 period
->start
.tm_mon
=month0
;
971 period
->start
.tm_year
=year0
-1900;
972 period
->start
.tm_hour
=hour0
;
973 period
->start
.tm_min
=minute0
;
974 period
->end
.tm_mday
=day1
;
975 period
->end
.tm_mon
=month1
;
976 period
->end
.tm_year
=year1
-1900;
977 period
->end
.tm_hour
=hour1
;
978 period
->end
.tm_min
=minute1
;
984 void getperiod_fromrange(struct periodstruct
*period
,int dfrom
,int duntil
)
986 memset(&period
->start
,0,sizeof(period
->start
));
987 period
->start
.tm_mday
=dfrom
%100;
988 period
->start
.tm_mon
=(dfrom
/100)%100-1;
989 period
->start
.tm_year
=(dfrom
/10000)-1900;
991 memset(&period
->end
,0,sizeof(period
->end
));
992 period
->end
.tm_mday
=duntil
%100;
993 period
->end
.tm_mon
=(duntil
/100)%100-1;
994 period
->end
.tm_year
=(duntil
/10000)-1900;
998 Update the \a main period to encompass the period in \a candidate.
1000 void getperiod_merge(struct periodstruct
*main
,struct periodstruct
*candidate
)
1005 mdate
=(main
->start
.tm_year
)*10000+(main
->start
.tm_mon
)*100+main
->start
.tm_mday
;
1006 cdate
=(candidate
->start
.tm_year
)*10000+(candidate
->start
.tm_mon
)*100+candidate
->start
.tm_mday
;
1007 if (cdate
<mdate
) memcpy(&main
->start
,&candidate
->start
,sizeof(struct tm
));
1009 mdate
=(main
->end
.tm_year
)*10000+(main
->end
.tm_mon
)*100+main
->end
.tm_mday
;
1010 cdate
=(candidate
->end
.tm_year
)*10000+(candidate
->end
.tm_mon
)*100+candidate
->end
.tm_mday
;
1011 if (cdate
>mdate
) memcpy(&main
->end
,&candidate
->end
,sizeof(struct tm
));
1014 int getperiod_buildtext(struct periodstruct
*period
)
1018 char text1
[40], text2
[40];
1021 i
=strftime(text1
, sizeof(text1
), "%Y %b %d", &period
->start
);
1022 } else if(df
=='e') {
1023 i
=strftime(text1
, sizeof(text1
), "%d %b %Y", &period
->start
);
1024 } else /*if (df=='w')*/ {
1025 IndexTree
=INDEX_TREE_FILE
;
1026 i
=strftime(text1
, sizeof(text1
), "%Y.%U", &period
->start
);
1028 if (i
== 0) return(-1);
1030 range
=(period
->start
.tm_year
!=period
->end
.tm_year
||
1031 period
->start
.tm_mon
!=period
->end
.tm_mon
||
1032 period
->start
.tm_mday
!=period
->end
.tm_mday
);
1035 i
=strftime(text2
, sizeof(text2
)-i
, "%Y %b %d", &period
->end
);
1036 } else if (df
=='e') {
1037 i
=strftime(text2
, sizeof(text2
)-i
, "%d %b %Y", &period
->end
);
1039 i
=strftime(text2
, sizeof(text2
)-i
, "%Y.%U", &period
->end
);
1041 if (i
== 0) return(-1);
1045 snprintf(period
->text
,sizeof(period
->text
),"%s-%s",text1
,text2
);
1046 snprintf(period
->html
,sizeof(period
->html
),"%s—%s",text1
,text2
);
1048 safe_strcpy(period
->text
,text1
,sizeof(period
->text
));
1049 safe_strcpy(period
->html
,text1
,sizeof(period
->html
));
1054 static void copy_images(void)
1056 FILE *img_in
, *img_ou
;
1058 char imgdir
[MAXLEN
];
1059 char srcfile
[MAXLEN
];
1060 char dstfile
[MAXLEN
];
1062 struct dirent
*direntp
;
1063 char buffer
[MAXLEN
];
1067 if (snprintf(images
,sizeof(images
),"%simages",outdir
)>=sizeof(images
)) {
1068 debuga(_("Cannot copy images to target directory %simages\n"),outdir
);
1071 if (access(images
,R_OK
)!=0) {
1072 if (mkdir(images
,0755)) {
1073 debuga(_("Cannot create directory %s - %s\n"),images
,strerror(errno
));
1078 strcpy(imgdir
,IMAGEDIR
);
1079 dirp
= opendir(imgdir
);
1081 debuga(_("(util) Can't open directory %s: %s\n"),imgdir
,strerror(errno
));
1084 while ((direntp
= readdir( dirp
)) != NULL
){
1085 if(direntp
->d_name
[0]=='.')
1087 if (snprintf(srcfile
,sizeof(srcfile
),"%s/%s",imgdir
,direntp
->d_name
)>=sizeof(srcfile
)) {
1088 debuga(_("Buffer too small to store %s/%s"),imgdir
,direntp
->d_name
);
1091 if (stat(srcfile
,&info
)) {
1092 debuga(_("Cannot stat \"%s\" - %s\n"),srcfile
,strerror(errno
));
1095 if (S_ISREG(info
.st_mode
)) {
1096 if (snprintf(dstfile
,sizeof(dstfile
),"%s/%s",images
,direntp
->d_name
)>=sizeof(dstfile
)) {
1097 debuga(_("Buffer too small to store %s/%s"),images
,direntp
->d_name
);
1100 img_in
= fopen(srcfile
, "rb");
1102 img_ou
= fopen(dstfile
, "wb");
1104 while ((nread
= fread(buffer
,1,sizeof(buffer
),img_in
))>0) {
1105 if (fwrite(buffer
,1,nread
,img_ou
)!=nread
) {
1106 debuga(_("Failed to copy image %s to %s\n"),srcfile
,dstfile
);
1110 if (fclose(img_ou
)==EOF
) {
1111 debuga(_("Error while copying image %s: %s\n"),dstfile
,strerror(errno
));
1115 fprintf(stderr
,"SARG: (util): %s %s: %s\n", _("Cannot open file")?_("Cannot open file"):"Can't open/create file", dstfile
, strerror(errno
));
1118 fprintf(stderr
,"SARG: (util): %s %s: %s\n", _("Cannot open file")?_("Cannot open file"):"Can't open file", srcfile
, strerror(errno
));
1121 (void) closedir(dirp
);
1127 * Check if the proposed file name conforms to the directory structure layed out
1128 * as a file tree. It is used to check if the file name enumerated while scanning
1129 * a directory content may have been created by sarg running with IndexTree set to
1132 bool IsTreeFileDirName(const char *Name
)
1137 // start year (date format u) or start day (date format e)
1138 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1140 if (isdigit(Name
[2]) && isdigit(Name
[3]))
1142 // date format is either u or w
1146 if (!isdigit(Name
[5]) || !isdigit(Name
[6])) return(false);
1147 return(true);//date format w is confirmed
1154 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1155 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1156 if (i
<0) return(false);
1160 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1165 else if (isalpha(Name
[2]) && isalpha(Name
[3]) && isalpha(Name
[4]))
1171 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1172 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1173 if (i
<0) return(false);
1177 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1185 if (Name
[0]!='-') return(false);
1188 if (DateFormat
=='u')
1190 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1193 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1194 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1195 if (i
<0) return(false);
1198 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1201 else //DateFormat=='e'
1203 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1206 if (!isalpha(Name
[0]) || !isalpha(Name
[1]) || !isalpha(Name
[2])) return(false);
1207 for (i
=11 ; i
>=0 && memcmp(mtab1
[i
],Name
,3) ; i
--);
1208 if (i
<0) return(false);
1211 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1215 * The directory name may contains additional characters such as a counter if
1216 * a previous report is never overwritten.
1222 * Check if the proposed file name can be the year part of a report tree build with
1223 * IndexTree set to INDEX_TREE_DATE.
1225 bool IsTreeYearFileName(const char *Name
)
1227 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1232 if (!isdigit(Name
[0]) || !isdigit(Name
[1]) || !isdigit(Name
[2]) || !isdigit(Name
[3])) return(false);
1235 if (Name
[0]) return(false);
1240 * Check if the proposed file name can be the month part of a report tree build with
1241 * IndexTree set to INDEX_TREE_DATE.
1243 bool IsTreeMonthFileName(const char *Name
)
1247 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1248 m
=(Name
[0]-'0')*10+(Name
[1]-'0');
1249 if (m
<1 || m
>12) return(false);
1254 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1255 m
=(Name
[0]-'0')*10+(Name
[1]-'0');
1256 if (m
<1 || m
>12) return(false);
1259 if (Name
[0]) return(false);
1264 * Check if the proposed file name can be the day part of a report tree build with
1265 * IndexTree set to INDEX_TREE_DATE.
1267 bool IsTreeDayFileName(const char *Name
)
1271 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1272 d
=(Name
[0]-'0')*10+(Name
[1]-'0');
1273 if (d
<1 || d
>31) return(false);
1277 if (!isdigit(Name
[0]) || !isdigit(Name
[1])) return(false);
1278 d
=(Name
[0]-'0')*10+(Name
[1]-'0');
1279 if (d
<1 || d
>31) return(false);
1282 * The directory name may contains additional characters such as a counter if
1283 * a previous report is never overwritten.
1288 int vrfydir(const struct periodstruct
*per1
, const char *addr
, const char *site
, const char *us
, const char *form
)
1293 char dirname2
[MAXLEN
];
1301 strcpy(wdir
,outdir
);
1303 y1
=per1
->start
.tm_year
+1900;
1304 y2
=per1
->end
.tm_year
+1900;
1305 m1
=per1
->start
.tm_mon
+1;
1306 m2
=per1
->end
.tm_mon
+1;
1307 d1
=per1
->start
.tm_mday
;
1308 d2
=per1
->end
.tm_mday
;
1309 if(IndexTree
== INDEX_TREE_DATE
) {
1310 wlen
+=sprintf(wdir
+wlen
,"%04d",y1
);
1311 if(y1
!=y2
) wlen
+=sprintf(wdir
+wlen
,"-%04d",y2
);
1312 if(access(wdir
, R_OK
) != 0)
1315 wlen
+=sprintf(wdir
+wlen
,"/%02d",m1
);
1316 if(m1
!= m2
) wlen
+=sprintf(wdir
+wlen
,"-%02d",m2
);
1317 if(access(wdir
, R_OK
) != 0)
1320 wlen
+=sprintf(wdir
+wlen
,"/%02d",d1
);
1321 if(d1
!=d2
) wlen
+=sprintf(wdir
+wlen
,"-%02d",d2
);
1324 wlen
=snprintf(wdir
+wlen
,sizeof(wdir
)-wlen
,"%04d%s%02d-%04d%s%02d",y1
,
1325 conv_month_name(m1
),d1
,y2
,conv_month_name(m2
),d2
);
1326 } else if (df
== 'e') {
1327 wlen
=snprintf(wdir
+wlen
,sizeof(wdir
)-wlen
,"%02d%s%04d-%02d%s%04d",d1
,
1328 conv_month_name(m1
),y1
,d2
,conv_month_name(m2
),y2
);
1329 } else if (df
== 'w') {
1330 wlen2
=strftime(wdir
+wlen
, sizeof(wdir
)-wlen
, "%Y.%U", &per1
->start
);
1331 if (wlen2
==0) return(-1);
1337 struct userinfostruct
*uinfo
=userinfo_find_from_id(us
);
1340 strcat(wdir
,uinfo
->filename
);
1343 if(addr
[0] != '\0') {
1347 if(site
[0] != '\0') {
1352 strcpy(outdirname
,wdir
);
1354 if(IndexTree
!= INDEX_TREE_DATE
) {
1355 if(!OverwriteReport
) {
1357 if(access(wdir
,R_OK
) == 0) {
1358 sprintf(wdir
,"%s.%d",outdirname
,num
);
1367 debuga(_("File %s already exists, moved to %s\n"),outdirname
,wdir
);
1368 rename(outdirname
,wdir
);
1371 if(access(outdirname
,R_OK
) == 0) {
1372 unlinkdir(outdirname
,1);
1375 my_mkdir(outdirname
);
1377 strcpy(dirname2
,wdir
);
1378 if(!OverwriteReport
) {
1380 if(access(wdir
,R_OK
) == 0) {
1381 sprintf(wdir
,"%s.%d",dirname2
,num
);
1390 debuga(_("File %s already exists, moved to %s\n"),dirname2
,wdir
);
1391 rename(dirname2
,wdir
);
1392 strcpy(dirname2
,wdir
);
1395 if(access(wdir
,R_OK
) == 0) {
1400 if(access(wdir
, R_OK
) != 0)
1404 strcpy(dirname2
,wdir
);
1406 if (snprintf(wdir
,sizeof(wdir
),"%s/sarg-date",outdirname
)>=sizeof(wdir
)) {
1407 debuga(_("Buffer too small to store %s/sarg-date"),outdirname
);
1410 if ((fp_ou
= fopen(wdir
, "wt")) == 0) {
1411 debuga(_("cannot open %s for writing: %s\n"),wdir
,strerror(errno
));
1416 //strftime(wdir,sizeof(wdir),"%a %b %d %H:%M:%S %Z %Y",localtime(&curtime));
1417 loctm
=localtime(&curtime
);
1418 strftime(wdir
,sizeof(wdir
),"%Y-%m-%d %H:%M:%S",loctm
);
1419 if (fprintf(fp_ou
,"%s %d\n",wdir
,loctm
->tm_isdst
)<0) {
1420 debuga(_("Failed to write the date in %s\n"),wdir
);
1424 if (fclose(fp_ou
)==EOF
) {
1425 debuga(_("Failed to write the date in %s: %s\n"),wdir
,strerror(errno
));
1434 Copy a string without overflowing the buffer. The copied string
1435 is properly terminated by an ASCII zero.
1437 \param dest The destination buffer.
1438 \param src The source buffer.
1439 \param length The size of the destination buffer. The program is aborted
1440 if the length is negative or zero.
1442 void safe_strcpy(char *dest
,const char *src
,int length
)
1445 debuga(_("Invalid buffer length passed to the function to safely copy a string\n"));
1448 strncpy(dest
,src
,length
-1);
1449 dest
[length
-1]='\0';
1452 void strip_latin(char *line
)
1459 for (i
=0;line
[i
];i
++){
1461 if (line
[i
]==';') skip
=0;
1473 void zdate(char *ftime
,int ftimesize
, char DateFormat
)
1479 local
= localtime(&t
);
1480 if (DateFormat
=='u')
1481 strftime(ftime
, ftimesize
, "%b/%d/%Y %H:%M", local
);
1482 else if (DateFormat
=='e')
1483 strftime(ftime
, ftimesize
, "%d/%b/%Y-%H:%M", local
);
1484 else if (DateFormat
=='w')
1485 strftime(ftime
, ftimesize
, "%W-%H-%M", local
);
1490 char *fixtime(long long int elap
)
1492 long int num
= elap
/ 1000LL;
1496 static char buf
[20];
1499 min
=(num
% 3600L) / 60L;
1502 if(hor
==0 && min
==0 && sec
==0)
1505 snprintf(buf
,sizeof(buf
),"%d:%02d:%02d",hor
,min
,sec
);
1511 void date_from(char *date
,int date_size
, int *dfrom
, int *duntil
)
1520 if (isdigit(date
[0])) {
1523 if (sscanf(date
,"%d/%d/%d%n",&d0
,&m0
,&y0
,&next
)!=3 || y0
<100 || m0
<1 || m0
>12 || d0
<1 || d0
>31 || next
<0) {
1524 debuga(_("The date passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1527 if (date
[next
]=='-') {
1528 if (sscanf(date
+next
+1,"%d/%d/%d",&d1
,&m1
,&y1
)!=3 || y1
<100 || m1
<1 || m1
>12 || d1
<1 || d1
>31) {
1529 debuga(_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1532 } else if (date
[next
]!='\0') {
1533 debuga(_("The date range passed as argument is not formated as dd/mm/yyyy or dd/mm/yyyy-dd/mm/yyyy\n"));
1543 struct tm
*Date0
,Date1
;
1545 if (time(&Today
)==(time_t)-1) {
1546 debuga(_("Failed to get the current time\n"));
1549 if (sscanf(date
,"day-%d",&i
)==1) {
1551 debuga(_("Invalid number of days in -d parameter\n"));
1555 Date0
=localtime(&Today
);
1557 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1560 y0
=y1
=Date0
->tm_year
+1900;
1561 m0
=m1
=Date0
->tm_mon
+1;
1562 d0
=d1
=Date0
->tm_mday
;
1563 } else if (sscanf(date
,"week-%d",&i
)==1) {
1565 There is no portable way to find the first day of the week even though the
1566 information is available in the locale. nl_langinfo has the unofficial
1567 parameters _NL_TIME_FIRST_WEEKDAY and _NL_TIME_WEEK_1STDAY but they are
1568 undocumented as is their return value and it is discouraged to use them.
1569 Beside, nl_langinfo isn't available on windows and the first day of the
1570 week isn't available at all on that system.
1572 const int FirstWeekDay
=1;
1576 debuga(_("Invalid number of weeks in -d parameter\n"));
1579 Date0
=localtime(&Today
);
1581 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1584 WeekBegin
=Today
-((Date0
->tm_wday
-FirstWeekDay
+7)%7)*24*60*60;
1585 WeekBegin
-=i
*7*24*60*60;
1586 Date0
=localtime(&WeekBegin
);
1588 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1591 y0
=Date0
->tm_year
+1900;
1594 WeekBegin
+=6*24*60*60;
1595 Date0
=localtime(&WeekBegin
);
1597 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1600 y1
=Date0
->tm_year
+1900;
1603 } else if (sscanf(date
,"month-%d",&i
)==1) {
1605 debuga(_("Invalid number of months in -d parameter\n"));
1608 Date0
=localtime(&Today
);
1610 debuga(_("Cannot convert local time: %s\n"),strerror(errno
));
1613 if (Date0
->tm_mon
<i
%12) {
1614 y0
=Date0
->tm_year
+1900-i
/12-1;
1615 m0
=(Date0
->tm_mon
+12-i
%12)%12+1;
1618 y0
=Date0
->tm_year
+1900-i
/12;
1619 m0
=Date0
->tm_mon
-i
%12+1;
1622 memcpy(&Date1
,Date0
,sizeof(struct tm
));
1627 Date1
.tm_year
=y0
-1900;
1630 Date1
.tm_year
=y0
-1900+1;
1634 Date0
=localtime(&t1
);
1635 y1
=Date0
->tm_year
+1900;
1639 debuga(_("Invalid date range passed on command line\n"));
1644 *dfrom
=y0
*10000+m0
*100+d0
;
1645 *duntil
=y1
*10000+m1
*100+d1
;
1646 snprintf(date
,date_size
,"%02d/%02d/%04d-%02d/%02d/%04d",d0
,m0
,y0
,d1
,m1
,y1
);
1651 char *strlow(char *string
)
1657 for (s
= string
; *s
; ++s
)
1667 char *strup(char *string
)
1673 for (s
= string
; *s
; ++s
)
1681 void removetmp(const char *outdir
)
1686 if(!RemoveTempFiles
)
1690 debuga(_("Purging temporary file sarg-general\n"));
1692 if (snprintf(filename
,sizeof(filename
),"%s/sarg-general",outdir
)>=sizeof(filename
)) {
1693 debuga(_("(removetmp) directory too long to remove %s/sarg-period\n"),outdir
);
1696 if((fp_gen
=fopen(filename
,"w"))==NULL
){
1697 debuga(_("(removetmp) Cannot open file %s: %s\n"),filename
,strerror(errno
));
1700 totalger(fp_gen
,filename
);
1701 if (fclose(fp_gen
)==EOF
) {
1702 debuga(_("Failed to close %s after writing the total line: %s\n"),filename
,strerror(errno
));
1707 void load_excludecodes(const char *ExcludeCodes
)
1715 if(ExcludeCodes
[0] == '\0')
1718 if((fp_in
=fopen(ExcludeCodes
,"r"))==NULL
) {
1719 debuga(_("(util) Cannot open file %s (exclude_codes): %s\n"),ExcludeCodes
,strerror(errno
));
1723 if (fseek(fp_in
, 0, SEEK_END
)==-1) {
1724 debuga(_("Failed to move till the end of the excluded codes file %s: %s\n"),ExcludeCodes
,strerror(errno
));
1727 MemSize
= ftell(fp_in
);
1729 debuga(_("Cannot get the size of file %s\n"),ExcludeCodes
);
1732 if (fseek(fp_in
, 0, SEEK_SET
)==-1) {
1733 debuga(_("Failed to rewind the excluded codes file %s: %s\n"),ExcludeCodes
,strerror(errno
));
1738 if((excludecode
=(char *) malloc(MemSize
))==NULL
) {
1739 debuga(_("malloc error (%ld)\n"),MemSize
);
1742 memset(excludecode
,0,MemSize
);
1745 while(fgets(data
,sizeof(data
),fp_in
)!=NULL
) {
1746 if (data
[0]=='#') continue;
1747 for (i
=strlen(data
)-1 ; i
>=0 && (unsigned char)data
[i
]<=' ' ; i
--) data
[i
]='\0';
1749 if (Stored
+i
+2>=MemSize
) {
1750 debuga(_("Too many codes to exclude in file %s\n"),ExcludeCodes
);
1753 strcat(excludecode
,data
);
1754 strcat(excludecode
,";");
1762 void free_excludecodes(void)
1770 int vercode(const char *code
)
1775 if (excludecode
&& excludecode
[0]!='\0') {
1779 if (strncmp(code
,cod
,clen
)==0 && cod
[clen
]==';')
1781 cod
=strchr(cod
,';');
1788 void fixnone(char *str
)
1792 for (i
=strlen(str
)-1 ; i
>=0 && (unsigned char)str
[i
]<=' ' ; i
--);
1793 if(i
==3 && strncmp(str
,"none",4) == 0)
1799 void fixendofline(char *str
)
1803 for (i
=strlen(str
)-1 ; i
>=0 && (unsigned char)str
[i
]<=' ' ; i
--) str
[i
]=0;
1806 #ifdef LEGACY_TESTVALIDUSERCHAR
1807 int testvaliduserchar(const char *user
)
1812 for (y
=0; y
<strlen(UserInvalidChar
); y
++) {
1813 for (x
=0; x
<strlen(user
); x
++) {
1814 if(user
[x
] == UserInvalidChar
[y
])
1821 int testvaliduserchar(const char *user
)
1823 char * p_UserInvalidChar
= UserInvalidChar
;
1824 const char * p_user
;
1826 while( *p_UserInvalidChar
) {
1829 if( *p_UserInvalidChar
== *p_user
)
1833 p_UserInvalidChar
++ ;
1839 int compar( const void *a
, const void *b
)
1841 if( *(int *)a
> *(int *)b
) return 1;
1842 if( *(int *)a
< *(int *)b
) return -1;
1846 int getnumlist( char *buf
, numlist
*list
, const int len
, const int maxvalue
)
1848 int i
, j
, d
, flag
, r1
, r2
;
1849 char *pbuf
, **bp
, *strbufs
[ 24 ];
1852 strtok( buf
, " \t" );
1853 for( *bp
= strtok( NULL
, "," ), list
->len
= 0; *bp
; *bp
= strtok( NULL
, "," ) ) {
1854 if( ++bp
>= &strbufs
[ 24 ] )
1861 for( i
= 0; i
< list
->len
; i
++ ) {
1862 if( strchr( strbufs
[ i
], '-' ) != 0 ) {
1863 pbuf
= strbufs
[ i
];
1864 strtok( pbuf
, "-" );
1865 pbuf
= strtok( NULL
, "\0" );
1866 r1
= atoi( strbufs
[ i
] );
1867 if( ( r2
= atoi( pbuf
) ) >= maxvalue
|| r1
>= r2
)
1869 if( i
+ d
+ ( r2
- r1
) + 1 <= len
) {
1870 for( j
= r1
; j
<= r2
; j
++ )
1871 list
->list
[ i
+ d
++ ] = j
;
1876 if( ( list
->list
[ i
+ d
] = atoi( strbufs
[ i
] ) ) >= maxvalue
)
1880 qsort( list
->list
, list
->len
, sizeof( int ), compar
);
1883 for( i
= 0; i
< list
->len
- 1; i
++ )
1884 if( list
->list
[ i
] == list
->list
[ i
+ 1 ] ) {
1885 for( j
= i
+ 1; j
< list
->len
; j
++ )
1886 list
->list
[ j
- 1 ] = list
->list
[ j
];
1896 char *get_size(const char *path
, const char *file
)
1899 static char response
[255];
1903 if (snprintf(cmd
,sizeof(cmd
),"du -skh %s%s",path
,file
)>=sizeof(cmd
)) {
1904 debuga(_("Cannot get disk space because the path %s%s is too long\n"),path
,file
);
1907 if ((fp
= popen(cmd
, "r")) == NULL
) {
1908 debuga(_("Cannot get disk space with command %s\n"),cmd
);
1911 if (!fgets(response
, sizeof(response
), fp
)) {
1912 debuga(_("Cannot get disk size with command %s\n"),cmd
);
1915 ptr
=strchr(response
,'\t');
1917 debuga(_("The command %s failed\n"),cmd
);
1926 void show_info(FILE *fp_ou
)
1930 if(!ShowSargInfo
) return;
1931 zdate(ftime
, sizeof(ftime
), df
);
1932 fputs("<div class=\"info\">",fp_ou
);
1933 fprintf(fp_ou
,_("Generated by <a href='%s'>%s-%s</a> on %s"),URL
,PGM
,VERSION
,ftime
);
1934 fputs("</div>\n",fp_ou
);
1937 void show_sarg(FILE *fp_ou
, int depth
)
1941 if(!ShowSargLogo
) return;
1942 fputs("<div class=\"logo\"><a href=\"http://sarg.sourceforge.net\"><img src=\"",fp_ou
);
1943 for (i
=0 ; i
<depth
; i
++)
1945 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
);
1948 void write_logo_image(FILE *fp_ou
)
1950 if(LogoImage
[0]!='\0')
1951 fprintf(fp_ou
, "<div class=\"logo\"><img src=\"%s\" width=\"%s\" height=\"%s\" alt=\"Logo\"> %s</div>\n",LogoImage
,Width
,Height
,LogoText
);
1954 void write_html_head(FILE *fp_ou
, int depth
, const char *page_title
,int javascript
)
1958 fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n",fp_ou
);
1959 fprintf(fp_ou
, "<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet
);
1960 if (page_title
) fprintf(fp_ou
,"<title>%s</title>\n",page_title
);
1962 if ((javascript
& HTML_JS_SORTTABLE
)!=0 && SortTableJs
[0]) {
1963 fputs("<script type=\"text/javascript\" src=\"",fp_ou
);
1964 if (strncmp(SortTableJs
,"../",3)==0) {
1965 for (i
=0 ; i
<depth
; i
++) fputs("../",fp_ou
);
1967 fputs(SortTableJs
,fp_ou
);
1968 fputs("\"></script>\n",fp_ou
);
1970 fputs("</head>\n<body>\n",fp_ou
);
1973 void write_html_header(FILE *fp_ou
, int depth
, const char *page_title
,int javascript
)
1975 write_html_head(fp_ou
,depth
,page_title
,javascript
);
1976 write_logo_image(fp_ou
);
1977 show_sarg(fp_ou
, depth
);
1978 fprintf(fp_ou
,"<div class=\"title\"><table cellpadding=\"0\" cellspacing=\"0\">\n<tr><th class=\"title_c\">%s</th></tr>\n",Title
);
1981 void close_html_header(FILE *fp_ou
)
1983 fputs("</table></div>\n",fp_ou
);
1986 int write_html_trailer(FILE *fp_ou
)
1989 if (fputs("</body>\n</html>\n",fp_ou
)==EOF
) return(-1);
1993 void output_html_string(FILE *fp_ou
,const char *str
,int maxlen
)
1997 while (*str
&& (maxlen
<=0 || i
<maxlen
)) {
2000 fputs("&",fp_ou
);
2003 fputs("<",fp_ou
);
2006 fputs(">",fp_ou
);
2009 fputs(""",fp_ou
);
2012 fputs("'",fp_ou
);
2020 if (maxlen
>0 && i
>=maxlen
)
2021 fputs("…",fp_ou
);
2024 void output_html_url(FILE *fp_ou
,const char *url
)
2028 fputs("&",fp_ou
);
2036 Write a host name inside an A tag of a HTML file. If the host name starts
2037 with a star, it is assumed to be an alias that cannot be put inside a link
2038 so the A tag is not written around the host name.
2040 \param fp_ou The handle of the HTML file.
2041 \param url The host to display in the HTML file.
2042 \param maxlen The maximum number of characters to print into the host name.
2044 void output_html_link(FILE *fp_ou
,const char *url
,int maxlen
)
2046 if (url
[0]==ALIAS_PREFIX
) {
2047 // this is an alias, no need for a A tag
2048 output_html_string(fp_ou
,url
+1,100);
2050 if (skip_scheme(url
)==url
)
2051 fputs("<a href=\"http://",fp_ou
);//no scheme in the url, assume http:// to make the link clickable
2053 fputs("<a href=\"",fp_ou
);//the scheme is in the url, no need to add one
2054 output_html_url(fp_ou
,url
);
2056 output_html_string(fp_ou
,url
,100);
2057 fputs("</a>",fp_ou
);
2061 void url_module(const char *url
, char *w2
)
2067 for(x
=strlen(url
)-1; x
>=0; x
--) {
2068 if(url
[x
] == '/' || y
>=sizeof(w
)-1) break;
2077 for(y
=y
-1; y
>=0; y
--) {
2084 Mangle an URL to produce a part that can be used as an anchor in
2085 a html <a name=""> tag.
2087 \param url The URL to mangle.
2088 \param anchor The buffer to write the mangled URL.
2089 \param size The size of the buffer.
2091 void url_to_anchor(const char *url
,char *anchor
,int size
)
2097 for (i
=0 ; url
[i
] && url
[i
]!='/' && url
[i
]!='?' ; i
++);
2104 // only keep really safe characters
2110 if(isalnum(url
[i
]) || url
[i
]=='-' || url
[i
]=='_' || url
[i
]=='.') {
2114 if (!skip
) anchor
[--j
]='_';
2132 printf(_("SARG Version: %s\n"),VERSION
);
2136 char *get_param_value(const char *param
,char *line
)
2140 while (*line
==' ' || *line
=='\t') line
++;
2142 if (strncasecmp(line
,param
,plen
)) return(NULL
);
2143 if (line
[plen
]!=' ' && line
[plen
]!='\t') return(NULL
);
2145 while (*line
==' ' || *line
=='\t') line
++;
2149 void unlinkdir(const char *dir
,bool contentonly
)
2153 struct dirent
*direntp
;
2159 while ((direntp
= readdir(dirp
)) != NULL
) {
2160 if (direntp
->d_name
[0] == '.' && (direntp
->d_name
[1] == '\0' ||
2161 (direntp
->d_name
[1] == '.' && direntp
->d_name
[2] == '\0')))
2163 if (snprintf(dname
,sizeof(dname
),"%s/%s",dir
,direntp
->d_name
)>=sizeof(dname
)) {
2164 debuga(_("directory name to delete too long: %s/%s\n"),dir
,direntp
->d_name
);
2168 err
=lstat(dname
,&st
);
2170 err
=stat(dname
,&st
);
2173 debuga(_("cannot stat %s\n"),dname
);
2176 if (S_ISREG(st
.st_mode
)) {
2177 if (unlink(dname
)) {
2178 debuga(_("Cannot delete \"%s\": %s\n"),dname
,strerror(errno
));
2181 } else if (S_ISDIR(st
.st_mode
)) {
2184 debuga(_("unknown path type %s\n"),dname
);
2191 debuga(_("Cannot delete \"%s\": %s\n"),dir
,strerror(errno
));
2198 Delete every file from the temporary directory where sarg is told to store its
2201 As any stray file left over by a previous run would be included in the report, we
2202 must delete every file from the temporary directory before we start processing the logs.
2204 But the temporary directory is given by the user either in the configuration file or
2205 on the command line. We check that the user didn't give a wrong directory by looking
2206 at the files stored in the directory. If a single file is not one of ours, we abort.
2208 \param dir The temporary directory to purge.
2210 void emptytmpdir(const char *dir
)
2214 struct dirent
*direntp
;
2220 static const char *TmpExt
[]=
2237 // make sure the temporary directory contains only our files
2238 while ((direntp
= readdir(dirp
)) != NULL
) {
2239 if (direntp
->d_name
[0] == '.' && (direntp
->d_name
[1] == '\0' ||
2240 (direntp
->d_name
[1] == '.' && direntp
->d_name
[2] == '\0')))
2243 // is it one of our files
2244 dlen
=strlen(direntp
->d_name
);
2245 for (i
=sizeof(TmpExt
)/sizeof(TmpExt
[0])-1 ; i
>=0 ; i
--) {
2246 elen
=strlen(TmpExt
[i
]);
2247 if (dlen
>=elen
&& strcasecmp(direntp
->d_name
+dlen
-elen
,TmpExt
[i
])==0) break;
2250 debuga(_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2251 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2252 "directory or manually delete the content of \"%s\"\n"),direntp
->d_name
,dir
,dir
);
2256 if (snprintf(dname
,sizeof(dname
),"%s/%s",dir
,direntp
->d_name
)>=sizeof(dname
)) {
2257 debuga(_("directory name to delete too long: %s/%s\n"),dir
,direntp
->d_name
);
2262 err
=lstat(dname
,&st
);
2264 err
=stat(dname
,&st
);
2267 debuga(_("cannot stat \"%s\"\n"),dname
);
2270 if (S_ISDIR(st
.st_mode
)) {
2272 } else if (!S_ISREG(st
.st_mode
)) {
2273 debuga(_("Unknown path type for \"%s\". Check temporary directory\n"),dname
);
2279 // now delete our files
2280 while ((direntp
= readdir(dirp
)) != NULL
) {
2281 if (direntp
->d_name
[0] == '.' && (direntp
->d_name
[1] == '\0' ||
2282 (direntp
->d_name
[1] == '.' && direntp
->d_name
[2] == '\0')))
2285 // is it one of our files
2286 dlen
=strlen(direntp
->d_name
);
2287 for (i
=sizeof(TmpExt
)/sizeof(TmpExt
[0])-1 ; i
>=0 ; i
--) {
2288 elen
=strlen(TmpExt
[i
]);
2289 if (dlen
>=elen
&& strcasecmp(direntp
->d_name
+dlen
-elen
,TmpExt
[i
])==0) break;
2292 debuga(_("Unknown file \"%s\" found in temporary directory \"%s\". It is not one of our files. "
2293 "Please check the temporary directory you gave to sarg. Adjust the path to a safe "
2294 "directory or manually delete the content of \"%s\"\n"),direntp
->d_name
,dir
,dir
);
2298 if (snprintf(dname
,sizeof(dname
),"%s/%s",dir
,direntp
->d_name
)>=sizeof(dname
)) {
2299 debuga(_("directory name to delete too long: %s/%s\n"),dir
,direntp
->d_name
);
2303 err
=lstat(dname
,&st
);
2305 err
=stat(dname
,&st
);
2308 debuga(_("cannot stat \"%s\"\n"),dname
);
2311 if (S_ISREG(st
.st_mode
)) {
2312 if (unlink(dname
)) {
2313 debuga(_("Cannot delete \"%s\": %s\n"),dname
,strerror(errno
));
2317 debuga(_("unknown path type %s\n"),dname
);
2324 Extract an url, IPv4 or IPv6 from a buffer. The IP addresses may end with a
2327 \param buf The buffer to parse.
2328 \param text A pointer to set to the beginning of the string pattern. No terminating zero is inserted.
2329 The pointer may be NULL.
2330 \param ipv4 A 4 bytes buffer to store the bytes of the IPv4 address.
2331 \param ipv6 A 8 short integers buffer to store the values of the IPv6 address.
2332 \param nbits The number of prefix bits for an IP address.
2333 \param next The content of the line after the extracted address.
2335 \retval 3 The pattern is a IPv6 address.
2336 \retval 2 The pattern is a IPv4 address.
2337 \retval 1 The patter is a string.
2338 \retval 0 Empty pattern.
2340 int extract_address_mask(const char *buf
,const char **text
,unsigned char *ipv4
,unsigned short int *ipv6
,int *nbits
,const char **next
)
2345 unsigned int value4
, value6
;
2346 unsigned short int addr
[8];
2356 // skip leading spaces and tabs
2357 while (*buf
&& (*buf
==' ' || *buf
=='\t')) buf
++;
2359 // find out the nature of the pattern
2360 ip_size
=0x60 | 0x04;
2371 for (i
=0 ; (unsigned char)buf
[i
]>' ' && buf
[i
]!='/' && buf
[i
]!='?' && (!bracket
|| buf
[i
]!=']') && ip_size
; i
++) {
2372 if (ip_size
& 0x04) {
2373 if (isdigit(buf
[i
])) {
2375 port_num
=port_num
*10+(buf
[i
]-'0');
2376 if (port_num
>65535) ip_size
&=~0x04;
2378 value4
=value4
*10+(buf
[i
]-'0');
2379 if (value4
>0xFFU
) ip_size
&=~0x04;
2381 } else if (buf
[i
]=='.' && addr_len
<4) {
2382 addr
[addr_len
++]=(unsigned short)(value4
& 0xFFU
);
2384 } else if (!port
&& buf
[i
]==':') {
2390 if (ip_size
& 0x60) {
2391 if (isdigit(buf
[i
])) {
2392 value6
=(value6
<<4)+(buf
[i
]-'0');
2394 if (value6
>0xFFFFU
) ip_size
&=~0x60;
2395 } else if (toupper(buf
[i
])>='A' && toupper(buf
[i
])<='F') {
2396 value6
=(value6
<<4)+(toupper(buf
[i
])-'A'+10);
2398 if (value6
>0xFFFFU
) ip_size
&=~0x60;
2399 } else if (buf
[i
]==':' && addr_len
<8) {
2400 if (nibble6_len
>0) {
2401 addr
[addr_len
++]=(unsigned short)(value6
& 0xFFFFU
);
2405 if (buf
[i
+1]==':') {
2414 if (i
==0) return(0);
2415 if (ip_size
& 0x04) {
2419 addr
[addr_len
++]=(unsigned short)(value4
& 0xFFU
);
2421 if (ip_size
& 0x60) {
2422 if (pad_pos
<0 && addr_len
!=7) {
2424 } else if (pad_pos
>=0 && addr_len
>=7)
2426 else if (nibble6_len
>0)
2427 addr
[addr_len
++]=(unsigned short)(value6
& 0xFFFFU
);
2432 if (bracket
) (*text
)--;
2434 while ((unsigned char)buf
[i
]>' ') i
++;
2435 if (next
) *next
=buf
+i
;
2438 max_mask
=(ip_size
& 0x04) ? 4*8 : 8*16;
2442 while (isdigit(buf
[i
])) i
++;
2443 if (mask
<0 || mask
>max_mask
) mask
=max_mask
;
2446 if (ip_size
& 0x60 && bracket
&& buf
[i
]==']') i
++;
2447 if (next
) *next
=buf
+i
;
2448 if (ip_size
& 0x04) {
2449 if (nbits
) *nbits
=mask
;
2450 for (i
=0 ; i
<addr_len
; i
++)
2451 ipv4
[i
]=(unsigned char)addr
[i
];
2456 if (nbits
) *nbits
=mask
;
2461 ipv6
[j
++]=(unsigned short int)addr
[i
++];
2463 while (j
<pad_pos
+pad_len
)
2467 ipv6
[j
++]=(unsigned short int)addr
[i
++];