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