]> git.ipfire.org Git - thirdparty/sarg.git/blame - download.c
Download report encapsulated in a module
[thirdparty/sarg.git] / download.c
CommitLineData
25697a35 1/*
94ff9470 2 * SARG Squid Analysis Report Generator http://sarg.sourceforge.net
61d965f3 3 * 1998, 2012
25697a35
GS
4 *
5 * SARG donations:
6 * please look at http://sarg.sourceforge.net/donations.php
1164c474
FM
7 * Support:
8 * http://sourceforge.net/projects/sarg/forums/forum/363374
25697a35
GS
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"
5f3cfd1d 28#include "include/defs.h"
25697a35 29
b2fa3eb6
FM
30/*!
31The buffer to store the list of the suffixes to take into account when generating
32the report of the downloaded files. The suffixes in the list are separated by the ASCII
33null.
34*/
2824ec9b 35/*@null@*/static char *DownloadSuffix=NULL;
b2fa3eb6
FM
36
37/*!
38The index of all the suffixes stored in ::DownloadSuffix. The list is sorted alphabetically.
39to speed up the search.
40*/
2824ec9b 41/*@null@*/static char **DownloadSuffixIndex=NULL;
b2fa3eb6
FM
42
43/*!
44The number of suffixes in ::DownloadSuffixIndex.
45*/
6e792ade
FM
46static int NDownloadSuffix=0;
47
11284535
FM
48//! Name of the file containing the unsorted downloaded entries.
49static char download_unsort[MAXLEN]="";
50//! The file handle to write the entries.
51static FILE *fp_download=NULL;
52//! \c True if at least one downloaded entry exists.
53static bool download_exists=false;
54
55/*!
56Open a file to store the denied accesses.
57
58\return The file handle or NULL if no file is necessary.
59*/
60void download_open(void)
61{
62 if ((ReportType & REPORT_TYPE_DOWNLOADS) == 0) {
63 if (debugz) debugaz(_("Download report not produced as it is not requested\n"));
64 return;
65 }
66 if (Privacy) {
67 if (debugz) debugaz(_("Download report not produced because privacy option is active\n"));
68 return;
69 }
70
71 snprintf(download_unsort,sizeof(download_unsort),"%s/download.int_unsort",tmp);
72 if ((fp_download=MY_FOPEN(download_unsort,"w"))==NULL) {
73 debuga(_("(log) Cannot open file: %s - %s\n"),download_unsort,strerror(errno));
74 exit(EXIT_FAILURE);
75 }
76 return;
77}
78
79/*!
80Write one entry in the unsorted downloaded file provided that it is required.
81
82\param log_entry The entry to write into the log file.
83\param url The URL of the downloaded file.
84*/
85void download_write(const struct ReadLogStruct *log_entry,const char *url)
86{
87 char date[80];
88
89 if (fp_download && strstr(log_entry->HttpCode,"DENIED") != 0) {
90 strftime(date,sizeof(date),"%d/%m/%Y\t%H:%M:%S",&log_entry->EntryTime);
91 fprintf(fp_download,"%s\t%s\t%s\t%s\n",date,log_entry->User,log_entry->Ip,url);
92 download_exists=true;
93 }
94}
95
96/*!
97Close the file opened by denied_open().
98*/
99void download_close(void)
100{
101 if (fp_download)
102 {
103 if (fclose(fp_download)==EOF)
104 {
105 debuga(_("Write error in %s: %s\n"),download_unsort,strerror(errno));
106 exit(EXIT_FAILURE);
107 }
108 fp_download=NULL;
109 }
110}
111
112/*!
113Tell the caller if a download report exists.
114
115\return \c True if the report is available or \c false if no report
116was generated.
117*/
118bool is_download(void)
119{
120 return(download_exists);
121}
122
b2fa3eb6
FM
123/*!
124Sort the raw log file with the downloaded files.
125
126\param report_in The name of the file where to store the sorted entries.
73b57f55
FM
127
128The file is sorted by columns 3, 1, 2 and 5 that are the columns of the user's ID, the
129date, the time and the URL.
b2fa3eb6
FM
130*/
131static void download_sort(const char *report_in)
132{
133 int clen;
134 char csort[MAXLEN];
135 int cstatus;
bd43d81f 136
11284535
FM
137 clen=snprintf(csort,sizeof(csort),"sort -T \"%s\" -t \"\t\" -k 3,3 -k 1,1 -k 2,2 -k 5,5 -o \"%s\" \"%s\"",
138 tmp, report_in, download_unsort);
b2fa3eb6 139 if (clen>=sizeof(csort)) {
11284535 140 debuga(_("Path too long to sort the file: %s\n"),download_unsort);
b2fa3eb6
FM
141 exit(EXIT_FAILURE);
142 }
143 cstatus=system(csort);
144 if (!WIFEXITED(cstatus) || WEXITSTATUS(cstatus)) {
145 debuga(_("sort command return status %d\n"),WEXITSTATUS(cstatus));
146 debuga(_("sort command: %s\n"),csort);
147 exit(EXIT_FAILURE);
148 }
11284535
FM
149 if (!KeepTempLog) {
150 if (unlink(download_unsort)) {
151 debuga(_("Cannot delete \"%s\": %s\n"),download_unsort,strerror(errno));
152 exit(EXIT_FAILURE);
153 }
154 download_unsort[0]='\0';
b2fa3eb6
FM
155 }
156}
157
158/*!
159Generate the report of the downloaded files. The list of the suffixes to take into account
160is set with set_download_suffix().
161*/
32e71fa4 162void download_report(void)
25697a35 163{
9bd92830
FM
164 FILE *fp_in = NULL, *fp_ou = NULL;
165
166 char *buf;
167 char *url;
168 char report_in[MAXLEN];
169 char report[MAXLEN];
170 char ip[MAXLEN];
171 char oip[MAXLEN];
172 char user[MAXLEN];
173 char ouser[MAXLEN];
174 char ouser2[MAXLEN];
175 char data[15];
176 char hora[15];
177 int z=0;
178 int count=0;
179 int i;
180 int day,month,year;
181 bool new_user;
182 struct getwordstruct gwarea;
183 longline line;
184 struct userinfostruct *uinfo;
185 struct tm t;
186
11284535
FM
187 if (!download_exists) {
188 if (!KeepTempLog && download_unsort[0]!='\0' && unlink(download_unsort))
189 debuga(_("Cannot delete \"%s\": %s\n"),download_unsort,strerror(errno));
190 download_unsort[0]='\0';
5589b847
FM
191 if (debugz) debugaz(_("No downloaded files to report\n"));
192 return;
193 }
194
9bd92830
FM
195 ouser[0]='\0';
196 ouser2[0]='\0';
197
b2fa3eb6 198 // sort the raw file
c98d6a0f 199 snprintf(report_in,sizeof(report_in),"%s/download.int_log",tmp);
b2fa3eb6 200 download_sort(report_in);
9bd92830 201
b2fa3eb6 202 // produce the report.
9bd92830
FM
203 snprintf(report,sizeof(report),"%s/download.html",outdirname);
204
205 if((fp_in=MY_FOPEN(report_in,"r"))==NULL) {
206 debuga(_("(download) Cannot open log file %s\n"),report_in);
207 exit(EXIT_FAILURE);
208 }
209
210 if((fp_ou=MY_FOPEN(report,"w"))==NULL) {
211 debuga(_("(download) Cannot open log file %s\n"),report);
212 exit(EXIT_FAILURE);
213 }
214
215 write_html_header(fp_ou,(IndexTree == INDEX_TREE_DATE) ? 3 : 1,_("Downloads"),HTML_JS_NONE);
216 fputs("<tr><td class=\"header_c\">",fp_ou);
217 fprintf(fp_ou,_("Period: %s"),period.html);
218 fputs("</td></tr>\n",fp_ou);
219 fprintf(fp_ou,"<tr><th class=\"header_c\">%s</th></tr>\n",_("Downloads"));
220 close_html_header(fp_ou);
221
222 fputs("<div class=\"report\"><table cellpadding=\"0\" cellspacing=\"2\">\n",fp_ou);
223 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></tr>\n",_("USERID"),_("IP/NAME"),_("DATE/TIME"),_("ACCESSED SITE"));
224
225 if ((line=longline_create())==NULL) {
226 debuga(_("Not enough memory to read the downloaded files\n"));
227 exit(EXIT_FAILURE);
228 }
229
230 while((buf=longline_read(fp_in,line))!=NULL) {
231 getword_start(&gwarea,buf);
232 if (getword(data,sizeof(data),&gwarea,'\t')<0 || getword(hora,sizeof(hora),&gwarea,'\t')<0 ||
007905af 233 getword(user,sizeof(user),&gwarea,'\t')<0 || getword(ip,sizeof(ip),&gwarea,'\t')<0) {
9bd92830
FM
234 debuga(_("There is a broken record or garbage in file %s\n"),report_in);
235 exit(EXIT_FAILURE);
236 }
237 if (getword_ptr(buf,&url,&gwarea,'\t')<0) {
238 debuga(_("There is a broken url in file %s\n"),report_in);
239 exit(EXIT_FAILURE);
240 }
241 if (sscanf(data,"%d/%d/%d",&day,&month,&year)!=3) continue;
242 computedate(year,month,day,&t);
243 strftime(data,sizeof(data),"%x",&t);
244
245 uinfo=userinfo_find_from_id(user);
246 if (!uinfo) {
247 debuga(_("Unknown user ID %s in file %s\n"),user,report_in);
248 exit(EXIT_FAILURE);
249 }
250 new_user=false;
251 if(!z) {
252 strcpy(ouser,user);
253 strcpy(oip,ip);
254 z++;
255 new_user=true;
256 } else {
257 if(strcmp(ouser,user) != 0) {
258 strcpy(ouser,user);
259 new_user=true;
260 }
261 if(strcmp(oip,ip) != 0) {
262 strcpy(oip,ip);
263 new_user=true;
264 }
265 }
266
267 if(DownloadReportLimit) {
268 if(strcmp(ouser2,uinfo->label) == 0) {
269 count++;
270 } else {
271 count=1;
272 strcpy(ouser2,uinfo->label);
273 }
274 if(count >= DownloadReportLimit)
275 continue;
276 }
277
278 for (i=strlen(url)-1 ; i>=0 && (unsigned char)url[i]<' ' ; i--) url[i]=0;
279
280 fputs("<tr>",fp_ou);
5138c1b9
FM
281 if (new_user) {
282 if (uinfo->topuser)
283 fprintf(fp_ou,"<td class=\"data\"><a href=\"%s/%s.html\">%s</a></td><td class=\"data\">%s</td>",uinfo->filename,uinfo->filename,uinfo->label,ip);
284 else
285 fprintf(fp_ou,"<td class=\"data\">%s</td><td class=\"data\">%s</td>",uinfo->label,ip);
286 } else
9bd92830
FM
287 fputs("<td class=\"data\"></td><td class=\"data\"></td>",fp_ou);
288 fprintf(fp_ou,"<td class=\"data\">%s-%s</td><td class=\"data2\">",data,hora);
67a93701 289 if(BlockIt[0]!='\0' && url[0]!=ALIAS_PREFIX) {
9bd92830
FM
290 fprintf(fp_ou,"<a href=\"%s%s?url=\"",wwwDocumentRoot,BlockIt);
291 output_html_url(fp_ou,url);
292 fprintf(fp_ou,"\"><img src=\"%s/sarg-squidguard-block.png\"></a>&nbsp;",ImageFile);
293 }
6fa33a32 294 output_html_link(fp_ou,url,100);
67a93701 295 fputs("</td></tr>\n",fp_ou);
9bd92830
FM
296 }
297 fclose(fp_in);
298 longline_destroy(&line);
299
300 fputs("</table></div>\n",fp_ou);
301 if (write_html_trailer(fp_ou)<0)
302 debuga(_("Write error in file %s\n"),report);
303 if (fclose(fp_ou)==EOF)
304 debuga(_("Failed to close file %s - %s\n"),report,strerror(errno));
305
11767c6a
FM
306 if (!KeepTempLog && unlink(report_in)) {
307 debuga(_("Cannot delete \"%s\": %s\n"),report_in,strerror(errno));
08f9b029
FM
308 exit(EXIT_FAILURE);
309 }
9bd92830
FM
310
311 return;
25697a35 312}
6e792ade 313
b2fa3eb6
FM
314/*!
315Free the memory allocated by set_download_suffix().
316*/
6e792ade
FM
317void free_download(void)
318{
9bd92830
FM
319 if (DownloadSuffix) {
320 free(DownloadSuffix);
321 DownloadSuffix=NULL;
322 }
323 if (DownloadSuffixIndex) {
324 free(DownloadSuffixIndex);
325 DownloadSuffixIndex=NULL;
326 }
327 NDownloadSuffix=0;
6e792ade
FM
328}
329
b2fa3eb6
FM
330/*!
331Set the list of the suffixes corresponding to the download of files you want to detect with
332is_download_suffix(). The list is sorted to make the search faster.
333
334\param list A comma separated list of the suffixes to set in ::DownloadSuffix.
335
336\note The memory allocated by this function must be freed by free_download().
337*/
6e792ade
FM
338void set_download_suffix(const char *list)
339{
9bd92830
FM
340 char *str;
341 int i, j, k;
342 int cmp;
343
344 free_download();
345
346 DownloadSuffix=strdup(list);
347 if (!DownloadSuffix) {
348 debuga(_("Download suffix list too long\n"));
349 exit(EXIT_FAILURE);
350 }
351 j = 1;
352 for (i=0 ; list[i] ; i++)
353 if (list[i] == ',') j++;
354 DownloadSuffixIndex=malloc(j*sizeof(char *));
355 if (!DownloadSuffixIndex) {
356 debuga(_("Too many download suffixes\n"));
357 exit(EXIT_FAILURE);
358 }
359
360 str = DownloadSuffix;
361 for (i=0 ; DownloadSuffix[i] ; i++) {
362 if (DownloadSuffix[i] == ',') {
363 DownloadSuffix[i] = '\0';
364 if (*str) {
365 cmp = -1;
366 for (j=0 ; j<NDownloadSuffix && (cmp=strcasecmp(str,DownloadSuffixIndex[j]))>0 ; j++);
367 if (cmp != 0) {
368 for (k=NDownloadSuffix ; k>j ; k--)
369 DownloadSuffixIndex[k]=DownloadSuffixIndex[k-1];
370 NDownloadSuffix++;
371 DownloadSuffixIndex[j]=str;
372 }
373 }
374 str=DownloadSuffix+i+1;
375 }
376 }
377
378 if (*str) {
379 cmp = -1;
380 for (j=0 ; j<NDownloadSuffix && (cmp=strcasecmp(str,DownloadSuffixIndex[j]))>0 ; j++);
381 if (cmp != 0) {
382 for (k=NDownloadSuffix ; k>j ; k--)
383 DownloadSuffixIndex[k]=DownloadSuffixIndex[k-1];
384 NDownloadSuffix++;
385 DownloadSuffixIndex[j]=str;
386 }
387 }
6e792ade
FM
388}
389
b2fa3eb6
FM
390/*!
391Tell if the URL correspond to a downloaded file. The function takes the extension at the end of the
392URL with a maximum of 9 characters and compare it to the list of the download suffix in
393::DownloadSuffix. If the suffix is found in the list, the function reports the URL as the download
394of a file.
395
396\param url The URL to test.
397
398\retval 1 The URL matches a suffix of a download.
399\retval 0 The URL is not a known download.
400
401\note A downloaded file cannot be detected if the file name is embedded in a GET or POST request. Only requests
402that ends with the file name can be detected.
403
404\note A URL embedding another web site's address ending by .com at the end of the URL will match the download
405extension com if it is defined in the ::DownloadSuffix.
406*/
2824ec9b 407bool is_download_suffix(const char *url)
6e792ade 408{
9bd92830
FM
409 int urllen;
410 int i;
411 int down, up, center;
412 const char *suffix;
413 int cmp;
414 const int max_suffix=10;
415
416 if (DownloadSuffix == NULL || NDownloadSuffix == 0) return(false);
417
418 urllen=strlen(url)-1;
419 if (urllen<=0) return(false);
420 if (url[urllen] == '.') return(false); //reject a single trailing dot
421 for (i=0 ; i<urllen && (url[i]!='/' || url[i+1]=='/') && url[i]!='?' ; i++);
422 if (i>=urllen) return(false); // url is a hostname without any path or file to download
423
424 for (i=0 ; i<=max_suffix && i<urllen && url[urllen-i]!='.' ; i++)
425 if (url[urllen-i] == '/' || url[urllen-i] == '?') return(false);
426 if (i>max_suffix || i>=urllen) return(false);
427
428 suffix=url+urllen-i+1;
429 down=0;
430 up=NDownloadSuffix-1;
431 while (down<=up) {
432 center=(down+up)/2;
433 cmp=strcasecmp(suffix,DownloadSuffixIndex[center]);
434 if (cmp == 0) return(true);
435 if (cmp < 0)
436 up = center-1;
437 else
438 down = center+1;
439 }
440 return(false);
6e792ade
FM
441}
442
11284535
FM
443/*!
444Remove any temporary file left by the download module.
445*/
446void download_cleanup(void)
447{
448 if (fp_download)
449 {
450 fclose(fp_download);
451 fp_download=NULL;
452 }
453 if (download_unsort[0]) {
454 if (unlink(download_unsort)==-1)
455 debuga(_("Failed to delete %s: %s\n"),download_unsort,strerror(errno));
456 }
457}