]> git.ipfire.org Git - thirdparty/sarg.git/blob - redirector.c
Strip suffix length is a global variable
[thirdparty/sarg.git] / redirector.c
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 #include "include/conf.h"
28 #include "include/defs.h"
29
30 static char **files_done = NULL;
31 static int nfiles_done = 0;
32
33 //! The number of invalid lines found in the redirector report.
34 static int RedirectorErrors=0;
35 //! The file containing the sorted entries.
36 static char redirector_sorted[MAXLEN]="";
37
38 extern char StripUserSuffix[MAX_USER_LEN];
39 extern int StripSuffixLen;
40
41 static void parse_log(FILE *fp_ou,char *buf,int dfrom,int duntil)
42 {
43 char leks[5], sep[2], res[MAXLEN];
44 char hour[15];
45 char source[128], list[128];
46 char full_url[MAX_URL_LEN];
47 const char *url;
48 char user[MAX_USER_LEN];
49 char ip[45];
50 char userlabel[MAX_USER_LEN];
51 long long int lmon, lday, lyear;
52 int mon, day, year;
53 int idata=0;
54 bool id_is_ip;
55 struct getwordstruct gwarea;
56 struct getwordstruct gwarea1;
57 struct userinfostruct *uinfo;
58
59 getword_start(&gwarea,buf);
60 if(RedirectorLogFormat[0] != '\0') {
61 getword_start(&gwarea1,RedirectorLogFormat);
62 leks[0]='\0';
63 if (getword(leks,sizeof(leks),&gwarea1,'#')<0) {
64 debuga(__FILE__,__LINE__,_("Invalid \"redirector_log_format\" option in your sarg.conf (too many characters before first tag)\n"));
65 exit(EXIT_FAILURE);
66 }
67 year=0;
68 mon=0;
69 day=0;
70 hour[0]='\0';
71 source[0]='\0';
72 list[0]='\0';
73 ip[0]='\0';
74 user[0]='\0';
75 full_url[0]='\0';
76 while(strcmp(leks,"end") != 0) {
77 if (getword(leks,sizeof(leks),&gwarea1,'#')<0) {
78 debuga(__FILE__,__LINE__,_("Invalid \"redirector_log_format\" option in your sarg.conf (missing # at end of tag)\n"));
79 exit(EXIT_FAILURE);
80 }
81 if (getword(sep,sizeof(sep),&gwarea1,'#')<0) {
82 debuga(__FILE__,__LINE__,_("Invalid \"redirector_log_format\" option in your sarg.conf (too many characters in column separator)\n"));
83 exit(EXIT_FAILURE);
84 }
85 if(strcmp(leks,"end") != 0) {
86 if (getword_limit(res,sizeof(res),&gwarea,sep[0])<0) {
87 debuga(__FILE__,__LINE__,_("Parsing of tag \"%s\" in redirector log \"%s\" returned no result\n"),leks,wentp);
88 RedirectorErrors++;
89 return;
90 }
91 if(strcmp(leks,"year") == 0) {
92 year=atoi(res);
93 } else if(strcmp(leks,"mon") == 0) {
94 mon=atoi(res);
95 } else if(strcmp(leks,"day") == 0) {
96 day=atoi(res);
97 } else if(strcmp(leks,"hour") == 0) {
98 if (strlen(res)>=sizeof(hour)) {
99 debuga(__FILE__,__LINE__,_("Hour string too long in redirector log file \"%s\"\n"),wentp);
100 RedirectorErrors++;
101 return;
102 }
103 strcpy(hour,res);
104 } else if(strcmp(leks,"source") == 0) {
105 if (strlen(res)>=sizeof(source)) {
106 debuga(__FILE__,__LINE__,_("Banning source name too long in redirector log file \"%s\"\n"),wentp);
107 RedirectorErrors++;
108 return;
109 }
110 strcpy(source,res);
111 } else if(strcmp(leks,"list") == 0) {
112 if (strlen(res)>=sizeof(list)) {
113 debuga(__FILE__,__LINE__,_("Banning list name too long in redirector log file \"%s\"\n"),wentp);
114 RedirectorErrors++;
115 return;
116 }
117 strcpy(list,res);
118 } else if(strcmp(leks,"ip") == 0) {
119 if (strlen(res)>=sizeof(ip)) {
120 debuga(__FILE__,__LINE__,_("IP address too long in redirector log file \"%s\"\n"),wentp);
121 RedirectorErrors++;
122 return;
123 }
124 strcpy(ip,res);
125 } else if(strcmp(leks,"user") == 0) {
126 if (strlen(res)>=sizeof(user)) {
127 debuga(__FILE__,__LINE__,_("User ID too long in redirector log file \"%s\"\n"),wentp);
128 RedirectorErrors++;
129 return;
130 }
131 strcpy(user,res);
132 } else if(strcmp(leks,"url") == 0) {
133 /*
134 * Don't worry about the url being truncated as we only keep the host name
135 * any way...
136 */
137 safe_strcpy(full_url,res,sizeof(full_url));
138 }
139 }
140 }
141 } else {
142 if (getword_atoll(&lyear,&gwarea,'-')<0 || getword_atoll(&lmon,&gwarea,'-')<0 ||
143 getword_atoll(&lday,&gwarea,' ')<0) {
144 debuga(__FILE__,__LINE__,_("Invalid date in file \"%s\"\n"),wentp);
145 RedirectorErrors++;
146 return;
147 }
148 year=(int)lyear;
149 mon=(int)lmon;
150 day=(int)lday;
151 if (getword(hour,sizeof(hour),&gwarea,' ')<0) {
152 debuga(__FILE__,__LINE__,_("Invalid time in file \"%s\"\n"),wentp);
153 RedirectorErrors++;
154 return;
155 }
156 if (getword_skip(MAXLEN,&gwarea,'(')<0 || getword(source,sizeof(source),&gwarea,'/')<0) {
157 debuga(__FILE__,__LINE__,_("Invalid redirected source in file \"%s\"\n"),wentp);
158 RedirectorErrors++;
159 return;
160 }
161 if (getword(list,sizeof(list),&gwarea,'/')<0) {
162 debuga(__FILE__,__LINE__,_("Invalid redirected list in file \"%s\"\n"),wentp);
163 RedirectorErrors++;
164 return;
165 }
166 if (getword_skip(MAXLEN,&gwarea,' ')<0 || getword_limit(full_url,sizeof(full_url),&gwarea,' ')<0) {
167 debuga(__FILE__,__LINE__,_("Invalid url in file \"%s\"\n"),wentp);
168 RedirectorErrors++;
169 return;
170 }
171 if (getword(ip,sizeof(ip),&gwarea,'/')<0) {
172 debuga(__FILE__,__LINE__,_("Invalid source IP in file \"%s\"\n"),wentp);
173 RedirectorErrors++;
174 return;
175 }
176 if (getword_skip(MAXLEN,&gwarea,' ')<0 || getword(user,sizeof(user),&gwarea,' ')<0) {
177 debuga(__FILE__,__LINE__,_("Invalid user in file \"%s\"\n"),wentp);
178 RedirectorErrors++;
179 return;
180 }
181 }
182 url=process_url(full_url,false);
183
184 //sprintf(warea,"%04d%02d%02d",year,mon,day);
185
186 if (RedirectorFilterOutDate) {
187 idata = year*10000+mon*100+day;
188 if(idata < dfrom || idata > duntil)
189 return;
190 }
191
192 if (StripSuffixLen>0)
193 {
194 int x=strlen(user);
195 if (x>StripSuffixLen && strcasecmp(user+(x-StripSuffixLen),StripUserSuffix)==0)
196 user[x-StripSuffixLen]='\0';
197 }
198 if(UserIp) {
199 strcpy(user,ip);
200 id_is_ip=true;
201 } else {
202 id_is_ip=false;
203 if (user[0]=='\0' || (user[1]=='\0' && (user[0]=='-' || user[0]==' '))) {
204 if(RecordsWithoutUser == RECORDWITHOUTUSER_IP) {
205 strcpy(user,ip);
206 id_is_ip=true;
207 }
208 if(RecordsWithoutUser == RECORDWITHOUTUSER_IGNORE)
209 return;
210 if(RecordsWithoutUser == RECORDWITHOUTUSER_EVERYBODY)
211 strcpy(user,"everybody");
212 }
213 }
214 uinfo=userinfo_find_from_id(user);
215 if (!uinfo) {
216 uinfo=userinfo_create(user,(id_is_ip) ? NULL : ip);
217 uinfo->no_report=true;
218 if(Ip2Name && id_is_ip) ip2name(user,sizeof(user));
219 user_find(userlabel,MAX_USER_LEN, user);
220 userinfo_label(uinfo,userlabel);
221 }
222 fprintf(fp_ou,"%s\t%04d%02d%02d\t%s\t%s\t%s\t",uinfo->id,year,mon,day,hour,ip,url);
223 if (source[0] && list[0])
224 fprintf(fp_ou,"%s/%s\n",source,list);
225 else if (source[0])
226 fprintf(fp_ou,"%s\n",source);
227 else
228 fprintf(fp_ou,"%s\n",list);
229 redirector_count++;
230 }
231
232 static void read_log(const char *wentp, FILE *fp_ou,int dfrom,int duntil)
233 {
234 FileObject *fp_in = NULL;
235 char *buf;
236 int i;
237 longline line;
238
239 if(debug) {
240 debuga(__FILE__,__LINE__,_("Reading redirector log file \"%s\"\n"),wentp);
241 }
242
243 /* With squidGuard, you can log groups in only one log file.
244 We must parse each log files only one time. Example :
245 dest porn {
246 domainlist porn/domains
247 urllist porn/urls
248 log file1.log
249 }
250 dest aggressive {
251 domainlist aggressive/domains
252 urllist aggressive/urls
253 log file2.log
254 }
255 dest audio-video {
256 domainlist audio-video/domains
257 urllist audio-video/urls
258 log file1.log
259 }
260 */
261 for (i=0; i<nfiles_done; i++)
262 if (!strcmp(wentp, files_done[i])) return;
263
264 nfiles_done++;
265 files_done = realloc(files_done, nfiles_done*sizeof(char *));
266 if (!files_done) {
267 debuga(__FILE__,__LINE__,_("Not enough memory to store the name of the new redirector log to be read - %s\n"),strerror(errno));
268 exit(EXIT_FAILURE);
269 }
270 files_done[nfiles_done-1] = strdup(wentp);
271 if (!files_done[nfiles_done-1]) {
272 debuga(__FILE__,__LINE__,_("Not enough memory to store the name of the new redirector log to be read - %s\n"),strerror(errno));
273 exit(EXIT_FAILURE);
274 }
275
276 if ((fp_in=FileObject_Open(wentp))==NULL) {
277 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),wentp,FileObject_GetLastOpenError());
278 exit(EXIT_FAILURE);
279 }
280
281 if ((line=longline_create())==NULL) {
282 debuga(__FILE__,__LINE__,_("Not enough memory to read file \"%s\"\n"),wentp);
283 exit(EXIT_FAILURE);
284 }
285
286 while ((buf=longline_read(fp_in,line)) != NULL) {
287 parse_log(fp_ou,buf,dfrom,duntil);
288 }
289 if (FileObject_Close(fp_in)) {
290 debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),wentp,FileObject_GetLastCloseError());
291 exit(EXIT_FAILURE);
292 }
293 longline_destroy(&line);
294 return;
295 }
296
297
298 void redirector_log(void)
299 {
300 FILE *fp_ou = NULL, *fp_guard = NULL;
301 char buf[MAXLEN];
302 char guard_in[MAXLEN];
303 char logdir[MAXLEN];
304 char user[MAXLEN];
305 char tmp6[MAXLEN];
306 int i;
307 int y;
308 int cstatus;
309 int dfrom, duntil;
310 char *str;
311 char *str2;
312
313 str2 = user;
314
315 if(SquidGuardConf[0] == '\0' && NRedirectorLogs == 0) {
316 if (debugz>=LogLevel_Process) debugaz(__FILE__,__LINE__,_("No redirector logs provided to produce that kind of report\n"));
317 return;
318 }
319
320 snprintf(guard_in,sizeof(guard_in),"%s/redirector.int_unsort",tmp);
321 if((fp_ou=fopen(guard_in,"w"))==NULL) {
322 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),guard_in,strerror(errno));
323 exit(EXIT_FAILURE);
324 }
325
326 dfrom=(period.start.tm_year+1900)*10000+(period.start.tm_mon+1)*100+period.start.tm_mday;
327 duntil=(period.end.tm_year+1900)*10000+(period.end.tm_mon+1)*100+period.end.tm_mday;
328
329 if (NRedirectorLogs>0) {
330 for (i=0 ; i<NRedirectorLogs ; i++)
331 read_log(RedirectorLogs[i],fp_ou,dfrom,duntil);
332 } else {
333 if(access(SquidGuardConf, R_OK) != 0) {
334 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),SquidGuardConf,strerror(errno));
335 exit(EXIT_FAILURE);
336 }
337
338 if((fp_guard=fopen(SquidGuardConf,"r"))==NULL) {
339 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),SquidGuardConf,strerror(errno));
340 exit(EXIT_FAILURE);
341 }
342
343 logdir[0]=0;
344 while(fgets(buf,sizeof(buf),fp_guard)!=NULL) {
345 fixendofline(buf);
346 if((str=get_param_value("logdir",buf))!=NULL) {
347 /*
348 We want to tolerate spaces inside the directory name but we must also
349 remove the trailing spaces left by the editor after the directory name.
350 This should not be a problem as nobody use a file name with trailing spaces.
351 */
352 for (y=strlen(str)-1 ; y>=0 && (unsigned char)str[y]<=' ' ; y--);
353 if (y>=sizeof(logdir)-1) y=sizeof(logdir)-2;
354 logdir[y+1] = '\0';
355 while (y>=0) {
356 logdir[y] = str[y];
357 y--;
358 }
359 } else if((str=get_param_value("log",buf))!=NULL) {
360 if((str2=get_param_value("anonymous",str))!=NULL)
361 str=str2;
362
363 /*
364 If logdir is defined, we prepend it to the log file name, otherwise, we assume
365 the log directive provides an absolute file name to the log file. Therefore,
366 we don't need to add an additionnal / at the beginning of the log file name.
367 */
368 y=(logdir[0]) ? sprintf(wentp,"%s/",logdir) : 0;
369 /*
370 Spaces are allowed in the name of the log file. The file name ends at the first #
371 because it is assumed it is an end of line comment. Any space before the # is then
372 removed. Any control character (i.e. a character with a code lower than 32) ends
373 the file name. That includes the terminating zero.
374 */
375 while((unsigned char)*str>=' ' && *str!='#' && y<sizeof(wentp)-1)
376 wentp[y++]=*str++;
377 if(*str=='#') {
378 str--;
379 while(*str==' ' && y>0) {
380 str--;
381 y--;
382 }
383 }
384 wentp[y]=0;
385 read_log(wentp,fp_ou,dfrom,duntil);
386 }
387 }
388 if (fclose(fp_guard)==EOF) {
389 debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),SquidGuardConf,strerror(errno));
390 exit(EXIT_FAILURE);
391 }
392 }
393
394 if (fp_ou && fclose(fp_ou)==EOF) {
395 debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),guard_in,strerror(errno));
396 exit(EXIT_FAILURE);
397 }
398
399 if (files_done) {
400 for (y=0; y<nfiles_done; y++)
401 if (files_done[y]) free(files_done[y]);
402 free(files_done);
403 }
404
405 if (redirector_count) {
406 snprintf(redirector_sorted,sizeof(redirector_sorted),"%s/redirector.int_log",tmp);
407 if(debug) {
408 debuga(__FILE__,__LINE__,_("Sorting file \"%s\"\n"),redirector_sorted);
409 }
410
411 if (snprintf(tmp6,sizeof(tmp6),"sort -t \"\t\" -k 1,1 -k 2,2 -k 4,4 \"%s\" -o \"%s\"",guard_in, redirector_sorted)>=sizeof(tmp6)) {
412 debuga(__FILE__,__LINE__,_("Sort command too long when sorting file \"%s\" to \"%s\"\n"),guard_in,redirector_sorted);
413 exit(EXIT_FAILURE);
414 }
415 cstatus=system(tmp6);
416 if (!WIFEXITED(cstatus) || WEXITSTATUS(cstatus)) {
417 debuga(__FILE__,__LINE__,_("sort command return status %d\n"),WEXITSTATUS(cstatus));
418 debuga(__FILE__,__LINE__,_("sort command: %s\n"),tmp6);
419 exit(EXIT_FAILURE);
420 }
421 }
422
423 if (!KeepTempLog && unlink(guard_in)) {
424 debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),guard_in,strerror(errno));
425 exit(EXIT_FAILURE);
426 }
427 return;
428 }
429
430 static void show_ignored_redirector(FILE *fp_ou,int count)
431 {
432 char ignored[80];
433
434 snprintf(ignored,sizeof(ignored),ngettext("%d more redirector entry not shown here&hellip;","%d more redirector entries not shown here&hellip;",count),count);
435 fprintf(fp_ou,"<tr><td class=\"data\"></td><td class=\"data\"></td><td class=\"data\"></td><td class=\"data2 more\">%s</td><td class=\"data\"></td></tr>\n",ignored);
436 }
437
438 void redirector_report(void)
439 {
440 FileObject *fp_in = NULL;
441 FILE *fp_ou = NULL;
442
443 char *buf;
444 char *url;
445 char report[MAXLEN];
446 char ip[45];
447 char rule[255];
448 char oip[45];
449 char user[MAXLEN];
450 char ouser[MAXLEN];
451 char data[15];
452 char hora[15];
453 char ouser2[255];
454 char oname[MAXLEN];
455 bool z=false;
456 int count=0;
457 long long int data2;
458 bool new_user;
459 struct getwordstruct gwarea;
460 const struct userinfostruct *uinfo;
461 struct tm t;
462 longline line;
463
464 ouser[0]='\0';
465 ouser2[0]='\0';
466
467 if(!redirector_count) {
468 if (debugz>=LogLevel_Process) {
469 if (redirector_sorted[0])
470 debugaz(__FILE__,__LINE__,_("Redirector report not generated because it is empty\n"));
471 }
472 return;
473 }
474
475 snprintf(report,sizeof(report),"%s/redirector.html",outdirname);
476
477 if((fp_in=FileObject_Open(redirector_sorted))==NULL) {
478 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),redirector_sorted,FileObject_GetLastOpenError());
479 exit(EXIT_FAILURE);
480 }
481
482 if((fp_ou=fopen(report,"w"))==NULL) {
483 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),report,strerror(errno));
484 exit(EXIT_FAILURE);
485 }
486
487 if ((line=longline_create())==NULL) {
488 debuga(__FILE__,__LINE__,_("Not enough memory to read file \"%s\"\n"),redirector_sorted);
489 exit(EXIT_FAILURE);
490 }
491
492 write_html_header(fp_ou,(IndexTree == INDEX_TREE_DATE) ? 3 : 1,_("Redirector report"),HTML_JS_NONE);
493 fputs("<tr><td class=\"header_c\">",fp_ou);
494 fprintf(fp_ou,_("Period: %s"),period.html);
495 fputs("</td></tr>\n",fp_ou);
496 fprintf(fp_ou,"<tr><th class=\"header_c\">%s</th></tr>\n",_("Redirector report"));
497 close_html_header(fp_ou);
498
499 fputs("<div class=\"report\"><table cellpadding=1 cellspacing=2>\n",fp_ou);
500 fprintf(fp_ou,"<tr><th class=\"header_l\">%s</th><th class=\"header_l\">%s</th><th class=\"header_l\">%s</th><th class=\"header_l\">%s</th><th class=\"header_l\">%s</th></tr>\n",_("USERID"),_("IP/NAME"),_("DATE/TIME"),_("ACCESSED SITE"),_("RULE"));
501
502 while((buf=longline_read(fp_in,line))!=NULL) {
503 getword_start(&gwarea,buf);
504 if (getword(user,sizeof(user),&gwarea,'\t')<0) {
505 debuga(__FILE__,__LINE__,_("Invalid user in file \"%s\"\n"),redirector_sorted);
506 exit(EXIT_FAILURE);
507 }
508 if (getword_atoll(&data2,&gwarea,'\t')<0) {
509 debuga(__FILE__,__LINE__,_("Invalid date in file \"%s\"\n"),redirector_sorted);
510 exit(EXIT_FAILURE);
511 }
512 if (getword(hora,sizeof(hora),&gwarea,'\t')<0) {
513 debuga(__FILE__,__LINE__,_("Invalid time in file \"%s\"\n"),redirector_sorted);
514 exit(EXIT_FAILURE);
515 }
516 if (getword(ip,sizeof(ip),&gwarea,'\t')<0) {
517 debuga(__FILE__,__LINE__,_("Invalid IP address in file \"%s\"\n"),redirector_sorted);
518 exit(EXIT_FAILURE);
519 }
520 if (getword_ptr(buf,&url,&gwarea,'\t')<0) {
521 debuga(__FILE__,__LINE__,_("Invalid url in file \"%s\"\n"),redirector_sorted);
522 exit(EXIT_FAILURE);
523 }
524 if (getword(rule,sizeof(rule),&gwarea,'\n')<0) {
525 debuga(__FILE__,__LINE__,_("Invalid rule in file \"%s\"\n"),redirector_sorted);
526 exit(EXIT_FAILURE);
527 }
528
529 uinfo=userinfo_find_from_id(user);
530 if (!uinfo) {
531 debuga(__FILE__,__LINE__,_("Unknown user ID %s in file \"%s\"\n"),user,redirector_sorted);
532 exit(EXIT_FAILURE);
533 }
534
535 computedate(data2/10000,(data2/100)%10,data2%100,&t);
536 strftime(data,sizeof(data),"%x",&t);
537
538 new_user=false;
539 if(!z) {
540 strcpy(ouser,user);
541 strcpy(oip,ip);
542 strcpy(oname,ip);
543 if (Ip2Name && !uinfo->id_is_ip) ip2name(oname,sizeof(oname));
544 z=true;
545 new_user=true;
546 } else {
547 if(strcmp(ouser,user) != 0) {
548 strcpy(ouser,user);
549 new_user=true;
550 }
551 if(strcmp(oip,ip) != 0) {
552 strcpy(oip,ip);
553 strcpy(oname,ip);
554 if (Ip2Name && !uinfo->id_is_ip) ip2name(oname,sizeof(oname));
555 new_user=true;
556 }
557 }
558
559 if(SquidGuardReportLimit) {
560 if(strcmp(ouser2,uinfo->label) == 0) {
561 count++;
562 } else {
563 if(count>SquidGuardReportLimit && SquidGuardReportLimit>0)
564 show_ignored_redirector(fp_ou,count-SquidGuardReportLimit);
565 count=1;
566 strcpy(ouser2,uinfo->label);
567 }
568 if(count > SquidGuardReportLimit)
569 continue;
570 }
571
572 if (new_user)
573 fprintf(fp_ou,"<tr><td class=\"data2\">%s</td><td class=\"data2\">%s</td>",uinfo->label,ip);
574 else
575 fputs("<tr><td class=\"data2\"></td><td class=\"data2\"></td>",fp_ou);
576 fprintf(fp_ou,"<td class=\"data2\">%s-%s</td><td class=\"data2\">",data,hora);
577 output_html_link(fp_ou,url,100);
578 fprintf(fp_ou,"</td><td class=\"data2\">%s</td></tr>\n",rule);
579 }
580 if (FileObject_Close(fp_in)) {
581 debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),redirector_sorted,FileObject_GetLastCloseError());
582 exit(EXIT_FAILURE);
583 }
584 longline_destroy(&line);
585
586 if(count>SquidGuardReportLimit && SquidGuardReportLimit>0)
587 show_ignored_redirector(fp_ou,count-SquidGuardReportLimit);
588
589 fputs("</table>\n",fp_ou);
590
591 if (RedirectorErrors>0)
592 {
593 fputs("<div class=\"warn\"><span>",fp_ou);
594 fprintf(fp_ou,ngettext("%d error found in the log file. Some entries may be missing.","%d errors found in the log file. Some entries may be missing.",RedirectorErrors),RedirectorErrors);
595 fputs("</span></div>\n",fp_ou);
596 }
597
598 fputs("</div>\n",fp_ou);
599 write_html_trailer(fp_ou);
600 if (fclose(fp_ou)==EOF) {
601 debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),report,strerror(errno));
602 exit(EXIT_FAILURE);
603 }
604
605 if (!KeepTempLog && unlink(redirector_sorted)) {
606 debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),redirector_sorted,strerror(errno));
607 exit(EXIT_FAILURE);
608 }
609
610 return;
611 }