]> git.ipfire.org Git - thirdparty/sarg.git/blob - index.c
Rename configure.in as configure.ac
[thirdparty/sarg.git] / index.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 #ifdef HAVE_LSTAT
31 #define MY_LSTAT lstat
32 #else
33 #define MY_LSTAT stat
34 #endif
35
36
37 static void make_date_index(void);
38 static void make_file_index(void);
39 static void file_index_to_date_index(const char *entry);
40 static void date_index_to_file_index(const char *entry);
41
42 void make_index(void)
43 {
44 DIR *dirp;
45 struct dirent *direntp;
46 char wdir[MAXLEN];
47
48 if(LastLog > 0) mklastlog(outdir);
49
50 if(Index == INDEX_NO) {
51 sprintf(wdir,"%s"INDEX_HTML_FILE,outdir);
52 if(access(wdir, R_OK) == 0) {
53 if (unlink(wdir)) {
54 debuga(__FILE__,__LINE__,_("Cannot delete \"%s\": %s\n"),wdir,strerror(errno));
55 exit(EXIT_FAILURE);
56 }
57 }
58 return;
59 }
60
61 if(debug) {
62 // TRANSLATORS: The %s is the name of the html index file (index.html).
63 debuga(__FILE__,__LINE__,_("Making %s\n"),INDEX_HTML_FILE);
64 }
65
66 // convert any old report hierarchy
67 if ((dirp = opendir(outdir)) == NULL) {
68 debuga(__FILE__,__LINE__,_("Cannot open directory \"%s\": %s\n"),outdir,strerror(errno));
69 exit(EXIT_FAILURE);
70 }
71 while ((direntp = readdir( dirp )) != NULL) {
72 if(isdigit(direntp->d_name[0]) && isdigit(direntp->d_name[1])) {
73 if(IndexTree == INDEX_TREE_DATE)
74 file_index_to_date_index(direntp->d_name);
75 else
76 date_index_to_file_index(direntp->d_name);
77 }
78 }
79 closedir(dirp);
80
81 if(IndexTree == INDEX_TREE_DATE) {
82 make_date_index();
83 } else {
84 make_file_index();
85 }
86 }
87
88 /*!
89 * Get the effective size of a regular file or directory.
90 *
91 * \param statb The structure filled by lstat(2).
92 *
93 * \return The size occupied on the disk (more or less).
94 *
95 * The actual size occupied on disk by a file or a directory table is not a
96 * trivial computation. It must take into account sparse files, compression,
97 * deduplication and probably many more.
98 *
99 * Here, we assume the file takes a whole number of blocks (which is not the
100 * case of ReiserFS); the block size is constant (which is not the case of
101 * ZFS); every data block is stored in one individal block (no deduplication as
102 * is done by btrfs); data are not compressed (unlike ReiserFS and ZFS).
103 *
104 * As we are dealing with directories containing mostly text and a few
105 * compressed pictures, we don't worry about sparse files with lot of zeros
106 * that would take less blocks than the actual file size.
107 */
108 static long long int get_file_size(struct stat *statb)
109 {
110 #ifdef __linux__
111 long long int blocks;
112
113 //return(statb->st_size);//the size of the file content
114 //return(statb->st_blocks*512);//what is the purpose of this size?
115 if (statb->st_blksize==0) return(statb->st_size);
116 blocks=(statb->st_size+statb->st_blksize-1)/statb->st_blksize;
117 return(blocks*statb->st_blksize);//how many bytes occupied on disk
118 #else
119 return(statb->st_size);
120 #endif
121 }
122
123 /*!
124 * Get the size of a directory.
125 *
126 * The size is the size of the directory content excluding the directory table.
127 * The "du" tool on Linux returns the content size including the directory
128 * table.
129 *
130 * \param path The directory whose size is computed. This is a buffer that must be
131 * big enough to contains the deepest path as directory entries are appended to
132 * the string this buffer contains.
133 * \param path_size The number of bytes available in the \a path buffer.
134 *
135 * \return The number of bytes occupied by the directory content.
136 */
137 static long long int get_size(char *path,int path_size)
138 {
139 int path_len;
140 DIR *dirp;
141 struct dirent *direntp;
142 struct stat statb;
143 int name_len;
144 long long int total_size=0;
145 char *dir_list=NULL;
146 int dir_filled=0;
147 int dir_allocated=0;
148
149 path_len=strlen(path);
150 if (path_len+2>=path_size) {
151 debuga(__FILE__,__LINE__,_("Path too long: "));
152 debuga_more("%s\n",path);
153 exit(EXIT_FAILURE);
154 }
155 if ((dirp=opendir(path))==NULL) {
156 debuga(__FILE__,__LINE__,_("Cannot open directory \"%s\": %s\n"),path,strerror(errno));
157 exit(EXIT_FAILURE);
158 }
159 path[path_len++]='/';
160 while ((direntp=readdir(dirp))!=NULL) {
161 if (direntp->d_name[0]=='.' && (direntp->d_name[1]=='\0' || (direntp->d_name[1]=='.' && direntp->d_name[2]=='\0'))) continue;
162 name_len=strlen(direntp->d_name);
163 if (path_len+name_len+1>=path_size) {
164 debuga(__FILE__,__LINE__,_("Path too long: "));
165 debuga_more("%s%s\n",path,direntp->d_name);
166 exit(EXIT_FAILURE);
167 }
168 strcpy(path+path_len,direntp->d_name);
169 if (MY_LSTAT(path,&statb) == -1) {
170 debuga(__FILE__,__LINE__,_("Failed to get the statistics of file \"%s\": %s\n"),path,strerror(errno));
171 continue;
172 }
173 if (S_ISDIR(statb.st_mode))
174 {
175 if (!dir_list || dir_filled+name_len>=dir_allocated)
176 {
177 int size=3*(name_len+1);//make room for three file names like this one
178 if (size<256) size=256;
179 dir_allocated+=size;
180 dir_list=realloc(dir_list,dir_allocated);
181 if (!dir_list) {
182 debuga(__FILE__,__LINE__,_("Not enough memory to recurse into subdirectory \"%s\"\n"),path);
183 exit(EXIT_FAILURE);
184 }
185 }
186 strcpy(dir_list+dir_filled,direntp->d_name);
187 dir_filled+=name_len+1;
188 total_size+=get_file_size(&statb);
189 }
190 else if (S_ISREG(statb.st_mode))
191 {
192 total_size+=get_file_size(&statb);
193 }
194 }
195 closedir(dirp);
196
197 if (dir_list)
198 {
199 int start=0;
200
201 while (start<dir_filled)
202 {
203 name_len=strlen(dir_list+start);
204 strcpy(path+path_len,dir_list+start);
205 total_size+=get_size(path,path_size);
206 start+=name_len+1;
207 }
208 free(dir_list);
209 }
210
211 path[path_len-1]='\0';//restore original string
212 return (total_size);
213 }
214
215 /*!
216 * Rebuild the html index file for a day when the reports are grouped in a date tree.
217 *
218 * \param monthdir The buffer containing the path where the html index file must be rebuild.
219 * The buffer must be big enough to contain the deepest path in that directory as the buffer is
220 * used to concatenate the directory entries.
221 * \param monthdir_size The size, in byte, of the \a monthdir buffer.
222 * \param order A postive number to sort the index file in positive order. A negative value sort it
223 * in decreasing order.
224 * \param yearnum The string naming the year in the date tree.
225 * \param monthnum The string naming the month in the date tree.
226 *
227 * \return The approximate size occupied by the directory.
228 */
229 static long long int make_date_index_day(char *monthdir,int monthdir_size,int order,const char *yearnum,const char *monthnum)
230 {
231 int monthdir_len;
232 int ndays;
233 DIR *dirp3;
234 struct dirent *direntp;
235 struct stat statb;
236 int i;
237 int daysort[31*31];
238 int d1, d2, day;
239 FILE *fp_ou;
240 char title[80];
241 char daynum[10];
242 int d;
243 long long int total_size=0;
244 long long int sub_size;
245 int name_len;
246
247 ndays=0;
248 if ((dirp3 = opendir(monthdir)) == NULL) {
249 debuga(__FILE__,__LINE__,_("Cannot open directory \"%s\": %s\n"),monthdir,strerror(errno));
250 exit(EXIT_FAILURE);
251 }
252 monthdir_len=strlen(monthdir);
253 if (monthdir_len+strlen(INDEX_HTML_FILE)+2>=monthdir_size) {
254 debuga(__FILE__,__LINE__,_("Path too long: "));
255 debuga_more("%s/%s\n",monthdir,INDEX_HTML_FILE);
256 exit(EXIT_FAILURE);
257 }
258 monthdir[monthdir_len++]='/';
259 while ((direntp = readdir( dirp3 )) != NULL) {
260 if (direntp->d_name[0]=='.' && (direntp->d_name[1]=='\0' || (direntp->d_name[1]=='.' && direntp->d_name[2]=='\0'))) continue;
261 name_len=strlen(direntp->d_name);
262 if (monthdir_len+name_len+1>=monthdir_size) {
263 debuga(__FILE__,__LINE__,_("Path too long: "));
264 debuga_more("%s%s\n",monthdir,direntp->d_name);
265 exit(EXIT_FAILURE);
266 }
267 strcpy(monthdir+monthdir_len,direntp->d_name);
268 if (MY_LSTAT(monthdir,&statb) == -1) {
269 debuga(__FILE__,__LINE__,_("Failed to get the statistics of file \"%s\": %s\n"),monthdir,strerror(errno));
270 continue;
271 }
272 if (S_ISDIR(statb.st_mode))
273 {
274 if(!isdigit(direntp->d_name[0]) && !isdigit(direntp->d_name[1])) continue;
275 i=-1;
276 if (sscanf(direntp->d_name,"%d%n",&d1,&i)!=1 || d1<1 || d1>31 || i<0) continue;
277 if (direntp->d_name[i]=='-') {
278 if (sscanf(direntp->d_name+i+1,"%d",&d2)!=1 || d2<1 || d2>31) continue;
279 } else if (direntp->d_name[i]!='\0') {
280 continue;
281 } else {
282 d2=0;
283 }
284 if (ndays>=sizeof(daysort)/sizeof(daysort[0])) {
285 debuga(__FILE__,__LINE__,_("Too many day directories in %s\nSupernumerary entries are ignored\n"),monthdir);
286 break;
287 }
288 day=(d1 << 5) | d2;
289 for (i=ndays ; i>0 && day<daysort[i-1] ; i--) {
290 daysort[i]=daysort[i-1];
291 }
292 daysort[i]=day;
293 ndays++;
294 total_size+=get_file_size(&statb);
295 }
296 else if (S_ISREG(statb.st_mode))
297 {
298 total_size+=get_file_size(&statb);
299 }
300 }
301 closedir(dirp3);
302
303 strcpy(monthdir+monthdir_len,INDEX_HTML_FILE);
304 if((fp_ou=fopen(monthdir,"w"))==NULL) {
305 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),monthdir,strerror(errno));
306 exit(EXIT_FAILURE);
307 }
308 snprintf(title,sizeof(title),ngettext("SARG: report for %s/%s","SARG: reports for %s/%s",ndays),yearnum,monthnum);
309 write_html_header(fp_ou,2,title,HTML_JS_NONE);
310 close_html_header(fp_ou);
311 fputs("<div class=\"index\"><table cellpadding=\"1\" cellspacing=\"2\">\n<tr><td></td><td></td></tr>\n",fp_ou);
312 fprintf(fp_ou,"<tr><th class=\"header_l\">%s/%s/%s</th>",_("YEAR"),_("MONTH"),_("DAYS"));
313 if (IndexFields & INDEXFIELDS_DIRSIZE)
314 fprintf(fp_ou,"<th class=\"header_l\">%s</th>",_("SIZE"));
315 fputs("</tr>\n",fp_ou);
316 for (d=0 ; d<ndays ; d++) {
317 if (order>0)
318 day=daysort[d];
319 else
320 day=daysort[ndays-1-d];
321 d1=(day >> 5) & 0x1F;
322 if ((day & 0x1F) != 0) {
323 d2=day & 0x1F;
324 sprintf(daynum,"%02d-%02d",d1,d2);
325 } else {
326 sprintf(daynum,"%02d",d1);
327 }
328 strcpy(monthdir+monthdir_len,daynum);
329 sub_size=get_size(monthdir,monthdir_size);
330
331 fprintf(fp_ou,"<tr><td class=\"data2\"><a href=\"%s/%s\">%s %s %s</a></td>",daynum,INDEX_HTML_FILE,yearnum,monthnum,daynum);
332 if (IndexFields & INDEXFIELDS_DIRSIZE)
333 {
334 char size_str[40];
335
336 strncpy(size_str,fixnum(sub_size,1),sizeof(size_str)-1);
337 size_str[sizeof(size_str)-1]='\0';
338 fprintf(fp_ou,"<td class=\"data2\">%s</td>",size_str);
339 }
340 fputs("</tr>\n",fp_ou);
341 total_size+=sub_size;
342 }
343 fputs("</table></div>\n",fp_ou);
344 monthdir[monthdir_len-1]='\0';
345 write_html_trailer(fp_ou);
346 if (fclose(fp_ou)==EOF) {
347 strcpy(monthdir+monthdir_len,INDEX_HTML_FILE);
348 debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),monthdir,strerror(errno));
349 exit(EXIT_FAILURE);
350 }
351 return(total_size);
352 }
353
354 /*!
355 * Get the name of a month based on its number.
356 *
357 * \param month The month number starting from one.
358 * \param month_name The buffer to store the month name.
359 * \param month_size The size of the \a month_name buffer.
360 */
361 static void name_month(int month,char *month_name,int month_size)
362 {
363 const char *m[12]={N_("January"),N_("February"),N_("March"),N_("April"),N_("May"),N_("June"),N_("July"),
364 N_("August"),N_("September"),N_("October"),N_("November"),N_("December")};
365
366 if (month<1 || month>12) {
367 debuga(__FILE__,__LINE__,_("The internal list of month names is invalid. Please report this bug to the translator.\n"));
368 exit(EXIT_FAILURE);
369 }
370 strncpy(month_name,_(m[month-1]),month_size-1);
371 month_name[month_size-1]='\0';
372 }
373
374 /*!
375 * Rebuild the html index file for a month when the reports are grouped in a date tree.
376 *
377 * \param yeardir The buffer containing the path where the html index file must be rebuild.
378 * The buffer must be big enough to contain the deepest path in that directory as the buffer is
379 * used to concatenate the directory entries.
380 * \param yeardir_size The size, in byte, of the \a yeardir buffer.
381 * \param order A postive number to sort the index file in positive order. A negative value sort it
382 * in decreasing order.
383 * \param yearnum The string naming the year in the date tree.
384 *
385 * \return The approximate size occupied by the directory.
386 */
387 static long long int make_date_index_month(char *yeardir,int yeardir_size,int order,const char *yearnum)
388 {
389 int yeardir_len;
390 int nmonths;
391 DIR *dirp2;
392 struct dirent *direntp;
393 struct stat statb;
394 int i;
395 int monthsort[144];
396 int m1, m2, month;
397 FILE *fp_ou;
398 char title[80];
399 char monthname1[9], monthname2[9];
400 char nmonth[30];
401 char monthnum[10];
402 int m;
403 long long int total_size=0;
404 long long int sub_size;
405 int name_len;
406
407 nmonths=0;
408 if ((dirp2 = opendir(yeardir)) == NULL) {
409 debuga(__FILE__,__LINE__,_("Cannot open directory \"%s\": %s\n"),yeardir,strerror(errno));
410 exit(EXIT_FAILURE);
411 }
412 yeardir_len=strlen(yeardir);
413 if (yeardir_len+strlen(INDEX_HTML_FILE)+2>=yeardir_size) {
414 debuga(__FILE__,__LINE__,_("Path too long: "));
415 debuga_more("%s/%s\n",yeardir,INDEX_HTML_FILE);
416 exit(EXIT_FAILURE);
417 }
418 yeardir[yeardir_len++]='/';
419 while ((direntp = readdir( dirp2 )) != NULL) {
420 if (direntp->d_name[0]=='.' && (direntp->d_name[1]=='\0' || (direntp->d_name[1]=='.' && direntp->d_name[2]=='\0'))) continue;
421 name_len=strlen(direntp->d_name);
422 if (yeardir_len+name_len+1>=yeardir_size) {
423 debuga(__FILE__,__LINE__,_("Path too long: "));
424 debuga_more("%s%s\n",yeardir,direntp->d_name);
425 exit(EXIT_FAILURE);
426 }
427 strcpy(yeardir+yeardir_len,direntp->d_name);
428 if (MY_LSTAT(yeardir,&statb) == -1) {
429 debuga(__FILE__,__LINE__,_("Failed to get the statistics of file \"%s\": %s\n"),yeardir,strerror(errno));
430 continue;
431 }
432 if (S_ISDIR(statb.st_mode))
433 {
434 if(!isdigit(direntp->d_name[0]) || !isdigit(direntp->d_name[1])) continue;
435 i=-1;
436 if (sscanf(direntp->d_name,"%d%n",&m1,&i)!=1 || m1<1 || m1>12 || i<0) continue;
437 if (direntp->d_name[i]=='-') {
438 if (sscanf(direntp->d_name+i+1,"%d",&m2)!=1 || m2<1 || m2>12) continue;
439 } else if (direntp->d_name[i]!='\0') {
440 continue;
441 } else {
442 m2=0;
443 }
444 if (nmonths>=sizeof(monthsort)/sizeof(monthsort[0])) {
445 debuga(__FILE__,__LINE__,_("Too many month directories in %s\nSupernumerary entries are ignored\n"),yeardir);
446 break;
447 }
448 month=(m1<<4) | m2;
449 for (i=nmonths ; i>0 && month<monthsort[i-1] ; i--) {
450 monthsort[i]=monthsort[i-1];
451 }
452 monthsort[i]=month;
453 nmonths++;
454 total_size+=get_file_size(&statb);
455 }
456 else if (S_ISREG(statb.st_mode))
457 {
458 total_size+=get_file_size(&statb);
459 }
460 }
461 closedir(dirp2);
462
463 strcpy(yeardir+yeardir_len,INDEX_HTML_FILE);
464 if((fp_ou=fopen(yeardir,"w"))==NULL) {
465 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),yeardir,strerror(errno));
466 exit(EXIT_FAILURE);
467 }
468 snprintf(title,sizeof(title),ngettext("SARG: report for %s","SARG: reports for %s",nmonths),yearnum);
469 write_html_header(fp_ou,1,title,HTML_JS_NONE);
470 close_html_header(fp_ou);
471 fputs("<div class=\"index\"><table cellpadding=\"1\" cellspacing=\"2\">\n<tr><td></td><td></td></tr>\n",fp_ou);
472 fprintf(fp_ou,"<tr><th class=\"header_l\">%s/%s</th>",_("YEAR"),_("MONTH"));
473 if (IndexFields & INDEXFIELDS_DIRSIZE)
474 fprintf(fp_ou,"<th class=\"header_l\">%s</th>",_("SIZE"));
475 fputs("</tr>\n",fp_ou);
476 for (m=0 ; m<nmonths ; m++) {
477 if (order>0)
478 month=monthsort[m];
479 else
480 month=monthsort[nmonths-1-m];
481 m1=(month >> 4) & 0x0F;
482 if ((month & 0x0F) != 0) {
483 m2=month & 0x0F;
484 sprintf(monthnum,"%02d-%02d",m1,m2);
485 name_month(m1,monthname1,sizeof(monthname1));
486 name_month(m2,monthname2,sizeof(monthname2));
487 sprintf(nmonth,"%s-%s",monthname1,monthname2);
488 } else {
489 sprintf(monthnum,"%02d",m1);
490 name_month(m1,nmonth,sizeof(nmonth));
491 }
492 if (yeardir_len+strlen(monthnum)+1>=yeardir_size) {
493 yeardir[yeardir_len]='\0';
494 debuga(__FILE__,__LINE__,_("Path too long: "));
495 debuga_more("%s%s\n",yeardir,monthnum);
496 exit(EXIT_FAILURE);
497 }
498 strcpy(yeardir+yeardir_len,monthnum);
499 sub_size=make_date_index_day(yeardir,yeardir_size,order,yearnum,nmonth);
500
501 fprintf(fp_ou,"<tr><td class=\"data2\"><a href=\"%s/%s\">%s %s</a></td>",monthnum,INDEX_HTML_FILE,yearnum,nmonth);
502 if (IndexFields & INDEXFIELDS_DIRSIZE)
503 {
504 char size_str[40];
505
506 strncpy(size_str,fixnum(sub_size,1),sizeof(size_str)-1);
507 size_str[sizeof(size_str)-1]='\0';
508 fprintf(fp_ou,"<td class=\"data2\">%s</td>",size_str);
509 }
510 fputs("</tr>\n",fp_ou);
511 total_size+=sub_size;
512 }
513 fputs("</table></div>\n",fp_ou);
514 yeardir[yeardir_len-1]='\0';
515 write_html_trailer(fp_ou);
516 if (fclose(fp_ou)==EOF) {
517 strcpy(yeardir+yeardir_len,INDEX_HTML_FILE);
518 debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),yeardir,strerror(errno));
519 exit(EXIT_FAILURE);
520 }
521 return(total_size);
522 }
523
524 /*!
525 * Rebuild a date index tree in the output directory.
526 */
527 static void make_date_index(void)
528 {
529 FILE *fp_ou;
530 DIR *dirp;
531 struct dirent *direntp;
532 char yearindex[MAXLEN];
533 char yeardir[MAXLEN];
534 char yearnum[10];
535 int yearsort[150];
536 int nyears;
537 int year;
538 int i, y;
539 int order;
540 int yeardirlen;
541 long long int total_size;
542
543 nyears=0;
544 if ((dirp = opendir(outdir)) == NULL) {
545 debuga(__FILE__,__LINE__,_("Cannot open directory \"%s\": %s\n"),outdir,strerror(errno));
546 exit(EXIT_FAILURE);
547 }
548 while ((direntp = readdir( dirp )) != NULL) {
549 if (!isdigit(direntp->d_name[0]) || !isdigit(direntp->d_name[1]) ||
550 !isdigit(direntp->d_name[2]) || !isdigit(direntp->d_name[3])) continue;
551 year=atoi(direntp->d_name) << 10;
552 if (direntp->d_name[4]=='-')
553 {
554 if (!isdigit(direntp->d_name[5]) || !isdigit(direntp->d_name[6]) ||
555 !isdigit(direntp->d_name[7]) || !isdigit(direntp->d_name[8])) continue;
556 if (direntp->d_name[9]) continue;
557 year|=atoi(direntp->d_name+5);
558 }
559 else
560 {
561 if (direntp->d_name[4]) continue;
562 }
563 if (nyears>=sizeof(yearsort)/sizeof(yearsort[0])) {
564 /*
565 If too many years are listed in the directory, we ignore the earliest years. The yearsort array
566 is big enough to accomodate the most ambitious use of sarg but this safety is added to prevent
567 a crash should the directory be polluted by other entries.
568 */
569 if (year>yearsort[0]) {
570 for (i=1 ; i<nyears && year>yearsort[i] ; i++)
571 yearsort[i-1]=yearsort[i];
572 yearsort[i-1]=year;
573 }
574 } else {
575 for (i=nyears ; i>0 && year<yearsort[i-1] ; i--) {
576 yearsort[i]=yearsort[i-1];
577 }
578 yearsort[i]=year;
579 nyears++;
580 }
581 }
582 closedir( dirp );
583
584 order=(strcmp(IndexSortOrder,"A") == 0) ? 1 : -1;
585
586 if (snprintf(yearindex,sizeof(yearindex),"%s"INDEX_HTML_FILE,outdir)>=sizeof(yearindex)) {
587 debuga(__FILE__,__LINE__,_("Resulting index file name too long. File name is \"%s/%s\""),outdir,INDEX_HTML_FILE);
588 exit(EXIT_FAILURE);
589 }
590 if((fp_ou=fopen(yearindex,"w"))==NULL) {
591 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),yearindex,strerror(errno));
592 exit(EXIT_FAILURE);
593 }
594 write_html_header(fp_ou,0,ngettext("SARG report","SARG reports",nyears),HTML_JS_NONE);
595 close_html_header(fp_ou);
596 fputs("<div class=\"index\"><table cellpadding=\"1\" cellspacing=\"2\">\n",fp_ou);
597 fprintf(fp_ou,"<tr><th class=\"header_l\">%s</th>",_("YEAR"));
598 if (IndexFields & INDEXFIELDS_DIRSIZE)
599 fprintf(fp_ou,"<th class=\"header_l\">%s</th>",_("SIZE"));
600 fputs("</tr>\n",fp_ou);
601
602 yeardirlen=strlen(outdir);
603 if (yeardirlen>=sizeof(yeardir)) {
604 debuga(__FILE__,__LINE__,_("Path too long: "));
605 debuga_more("%s",outdir);
606 exit(EXIT_FAILURE);
607 }
608 strcpy(yeardir,outdir);
609
610 for (y=0 ; y<nyears ; y++) {
611 if (order>0)
612 year=yearsort[y];
613 else
614 year=yearsort[nyears-1-y];
615 if ((year & 0x3FF)==0)
616 sprintf(yearnum,"%04d",year>>10);
617 else
618 sprintf(yearnum,"%04d-%04d",year>>10,year & 0x3FF);
619 strcpy(yeardir+yeardirlen,yearnum);
620 total_size=make_date_index_month(yeardir,sizeof(yeardir),order,yearnum);
621
622 fprintf(fp_ou,"<tr><td class=\"data2\"><a href=\"%s/%s\">%s</a></td>",yearnum,INDEX_HTML_FILE,yearnum);
623 if (IndexFields & INDEXFIELDS_DIRSIZE)
624 {
625 char size_str[40];
626
627 strncpy(size_str,fixnum(total_size,1),sizeof(size_str)-1);
628 size_str[sizeof(size_str)-1]='\0';
629 fprintf(fp_ou,"<td class=\"data2\">%s</td>",size_str);
630 }
631 fputs("</tr>\n",fp_ou);
632 }
633
634 fputs("</table></div>\n",fp_ou);
635 write_html_trailer(fp_ou);
636 if (fclose(fp_ou)==EOF) {
637 debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),yearindex,strerror(errno));
638 exit(EXIT_FAILURE);
639 }
640 }
641
642 static void make_file_index(void)
643 {
644 #define MAX_CREATION_DATE 15
645 FILE *fp_ou;
646 DIR *dirp;
647 struct dirent *direntp;
648 char wdir[MAXLEN];
649 char data[80];
650 char ftime[128];
651 char day[6], mon[8], year[40], hour[10];
652 long long int tbytes;
653 long long int media;
654 int iyear, imonth, iday, ihour, iminute, isecond, idst;
655 int nsort;
656 int nallocated;
657 int order;
658 int i;
659 int tuser;
660 struct getwordstruct gwarea;
661 struct sortstruct
662 {
663 int year, month, day, sortnum;
664 char creationdate[MAX_CREATION_DATE];
665 char *dirname;
666 char date[60];
667 } **sortlist, *item, **tempsort;
668
669 sprintf(wdir,"%s"INDEX_HTML_FILE,outdir);
670
671 order=(strcmp(IndexSortOrder,"A") == 0) ? 1 : -1;
672
673 if ((dirp = opendir(outdir)) == NULL) {
674 debuga(__FILE__,__LINE__,_("Cannot open directory \"%s\": %s\n"),outdir,strerror(errno));
675 exit(EXIT_FAILURE);
676 }
677
678 nsort=0;
679 nallocated=0;
680 sortlist=NULL;
681 while ((direntp = readdir( dirp )) != NULL) {
682 if (strchr(direntp->d_name,'-') == 0) continue;
683 if (obtdate(outdir,direntp->d_name,data)<0) {
684 debuga(__FILE__,__LINE__,_("The directory \"%s%s\" looks like a report directory but doesn't contain a sarg-date file. You should delete it\n"),outdir,direntp->d_name);
685 continue;
686 }
687 item=malloc(sizeof(*item));
688 if (!item) {
689 debuga(__FILE__,__LINE__,_("not enough memory to sort the index\n"));
690 exit(EXIT_FAILURE);
691 }
692 if (df=='u') {
693 item->year=atoi(direntp->d_name);
694 item->month=conv_month(direntp->d_name+4);
695 item->day=atoi(direntp->d_name+7);
696 } else {
697 item->year=atoi(direntp->d_name+5);
698 item->month=conv_month(direntp->d_name+2);
699 item->day=atoi(direntp->d_name);
700 }
701 item->sortnum=(item->year*16+item->month)*32+item->day;
702 if (sscanf(data,"%d-%d-%d %d:%d:%d %d",&iyear,&imonth,&iday,&ihour,&iminute,&isecond,&idst)==7) {
703 formatdate(data,sizeof(data),iyear,imonth,iday,ihour,iminute,isecond,idst);
704 snprintf(item->creationdate,sizeof(item->creationdate),"%04d%02d%02d%02d%02d%02d",iyear,imonth,iday,ihour,iminute,isecond);
705 } else {
706 /*
707 Old code to parse a date stored by sarg before 2.2.6.1 in the sarg-date file of each report directory.
708 */
709 getword_start(&gwarea,data);
710 if (getword_skip(16,&gwarea,' ')<0) {
711 debuga(__FILE__,__LINE__,_("Invalid date in file \"%s%s/sarg-date\"\n"),outdir,direntp->d_name);
712 exit(EXIT_FAILURE);
713 }
714 if (getword_multisep(mon,sizeof(mon),&gwarea,' ')<0) {
715 debuga(__FILE__,__LINE__,_("Invalid date in file \"%s%s/sarg-date\"\n"),outdir,direntp->d_name);
716 exit(EXIT_FAILURE);
717 }
718 if (getword_multisep(day,sizeof(day),&gwarea,' ')<0) {
719 debuga(__FILE__,__LINE__,_("Invalid date in file \"%s%s/sarg-date\"\n"),outdir,direntp->d_name);
720 exit(EXIT_FAILURE);
721 }
722 if (getword_multisep(hour,sizeof(hour),&gwarea,' ')<0) {
723 debuga(__FILE__,__LINE__,_("Invalid time in file \"%s%s/sarg-date\"\n"),outdir,direntp->d_name);
724 exit(EXIT_FAILURE);
725 }
726 do {
727 if (getword_multisep(year,sizeof(year),&gwarea,' ')<0) {
728 debuga(__FILE__,__LINE__,_("Invalid date in file \"%s%s/sarg-date\"\n"),outdir,direntp->d_name);
729 exit(EXIT_FAILURE);
730 }
731 } while (year[0] && !isdigit(year[0])); //skip time zone information with spaces until the year is found
732 if (sscanf(hour,"%d:%d:%d",&ihour,&iminute,&isecond)!=3) {
733 debuga(__FILE__,__LINE__,_("Invalid time in file \"%s%s/sarg-date\"\n"),outdir,direntp->d_name);
734 exit(EXIT_FAILURE);
735 }
736 buildymd(day,mon,year,ftime,sizeof(ftime));
737 snprintf(item->creationdate,sizeof(item->creationdate),"%s%02d%02d%02d",ftime, ihour, iminute, isecond);
738 }
739 item->dirname=strdup(direntp->d_name);
740 if (!item->dirname) {
741 debuga(__FILE__,__LINE__,_("Not enough memory to store the directory name \"%s\" in the index\n"),direntp->d_name);
742 exit(EXIT_FAILURE);
743 }
744 safe_strcpy(item->date,data,sizeof(item->date));
745 if (nsort+1>nallocated) {
746 nallocated+=10;
747 tempsort=realloc(sortlist,nallocated*sizeof(*item));
748 if (!tempsort) {
749 debuga(__FILE__,__LINE__,_("not enough memory to sort the index\n"));
750 exit(EXIT_FAILURE);
751 }
752 sortlist=tempsort;
753 }
754 for (i=nsort ; i>0 ; i--) {
755 if (item->sortnum>sortlist[i-1]->sortnum) break;
756 if (item->sortnum==sortlist[i-1]->sortnum) {
757 if (strcmp(item->creationdate,sortlist[i-1]->creationdate)>=0) break;
758 }
759 sortlist[i]=sortlist[i-1];
760 }
761 sortlist[i]=item;
762 nsort++;
763 }
764
765 closedir( dirp );
766
767 if((fp_ou=fopen(wdir,"w"))==NULL) {
768 debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),wdir,strerror(errno));
769 exit(EXIT_FAILURE);
770 }
771 write_html_header(fp_ou,0,ngettext("SARG report","SARG reports",nsort),HTML_JS_SORTTABLE);
772 close_html_header(fp_ou);
773 fputs("<div class=\"index\"><table cellpadding=\"1\" cellspacing=\"2\"",fp_ou);
774 if (SortTableJs[0]) fputs(" class=\"sortable\"",fp_ou);
775 fputs(">\n",fp_ou);
776 fprintf(fp_ou,"<thead><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></thead>\n",
777 _("FILE/PERIOD"),_("CREATION DATE"),_("USERS"),_("BYTES"),_("AVERAGE"));
778 for (i=0 ; i<nsort ; i++) {
779 if (order>0)
780 item=sortlist[i];
781 else
782 item=sortlist[nsort-i-1];
783 tuser=obtuser(outdir,item->dirname);
784 obttotal(outdir,item->dirname,tuser,&tbytes,&media);
785 fputs("<tr><td class=\"data2\"",fp_ou);
786 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%d\"",item->sortnum);
787 fprintf(fp_ou,"><a href='%s/%s'>%s</a></td>",item->dirname,ReplaceIndex,item->dirname);
788 fputs("<td class=\"data2\"",fp_ou);
789 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%s\"",item->creationdate);
790 fprintf(fp_ou,">%s</td>",item->date);
791 fprintf(fp_ou,"<td class=\"data\">%d</td>",tuser);
792 fputs("<td class=\"data\"",fp_ou);
793 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%"PRId64"\"",(int64_t)tbytes);
794 fprintf(fp_ou,">%s</td>",fixnum(tbytes,1));
795 fputs("<td class=\"data\"",fp_ou);
796 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%"PRId64"\"",(int64_t)media);
797 fprintf(fp_ou,">%s</td></tr>\n",fixnum(media,1));
798 }
799 fputs("</table></div>\n",fp_ou);
800 write_html_trailer(fp_ou);
801 if (fclose(fp_ou)==EOF)
802 debuga(__FILE__,__LINE__,_("Write error in \"%s\": %s\n"),wdir,strerror(errno));
803
804 if (sortlist) {
805 for (i=0 ; i<nsort ; i++) {
806 free(sortlist[i]->dirname);
807 free(sortlist[i]);
808 }
809 free(sortlist);
810 }
811 }
812
813 static void file_index_to_date_index(const char *entry)
814 {
815 int y1, y2, m1, m2, d1, d2;
816 int i, j;
817 int ndirlen;
818 int monthlen;
819 char sm1[8], sm2[8];
820 char olddir[MAXLEN], newdir[MAXLEN];
821
822 if(strlen(entry) < 19) return;
823
824 y1=0;
825 y2=0;
826 memset(sm1,0,sizeof(sm1));
827 memset(sm2,0,sizeof(sm2));
828 d1=0;
829 d2=0;
830 i=0;
831 if (df=='u') {
832 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
833 y1=y1*10+(entry[i++]-'0');
834 if (j!=4) return;
835 for (j=0 ; j<sizeof(sm1)-1 && entry[i] && isalpha(entry[i]) ; j++)
836 sm1[j]=entry[i++];
837 if (j!=3) return;
838 sm1[j]='\0';
839 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
840 d1=d1*10+(entry[i++]-'0');
841 if (j!=2) return;
842
843 if (entry[i++]!='-') return;
844
845 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
846 y2=y2*10+(entry[i++]-'0');
847 if (j!=4) return;
848 for (j=0 ; j<sizeof(sm2)-1 && entry[i] && isalpha(entry[i]) ; j++)
849 sm2[j]=entry[i++];
850 if (j!=3) return;
851 sm2[j]='\0';
852 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
853 d2=d2*10+(entry[i++]-'0');
854 if (j!=2) return;
855 } else if (df=='e') {
856 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
857 d1=d1*10+(entry[i++]-'0');
858 if (j!=2) return;
859 for (j=0 ; j<sizeof(sm1)-1 && entry[i] && isalpha(entry[i]) ; j++)
860 sm1[j]=entry[i++];
861 if (j!=3) return;
862 sm1[j]='\0';
863 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
864 y1=y1*10+(entry[i++]-'0');
865 if (j!=4) return;
866
867 if (entry[i++]!='-') return;
868
869 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
870 d2=d2*10+(entry[i++]-'0');
871 if (j!=2) return;
872 for (j=0 ; j<sizeof(sm2)-1 && entry[i] && isalpha(entry[i]) ; j++)
873 sm2[j]=entry[i++];
874 if (j!=3) return;
875 sm2[j]='\0';
876 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
877 y2=y2*10+(entry[i++]-'0');
878 if (j!=4) return;
879 } else
880 return;
881
882 m1=conv_month(sm1);
883 m2=conv_month(sm2);
884 ndirlen=sprintf(newdir,"%s%04d",outdir,y1);
885 if (access(newdir, R_OK) != 0) {
886 if (PortableMkDir(newdir,0755)) {
887 debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),newdir,strerror(errno));
888 exit(EXIT_FAILURE);
889 }
890 }
891 if(m1 != m2) ndirlen+=sprintf(newdir+ndirlen,"/%02d-%02d",m1,m2);
892 else ndirlen+=sprintf(newdir+ndirlen,"/%02d",m1);
893 if (access(newdir, R_OK) != 0) {
894 if (PortableMkDir(newdir,0755)) {
895 debuga(__FILE__,__LINE__,_("Cannot create directory \"%s\": %s\n"),newdir,strerror(errno));
896 exit(EXIT_FAILURE);
897 }
898 }
899 monthlen=ndirlen;
900 if(d1!=d2) ndirlen+=sprintf(newdir+ndirlen,"/%02d-%02d",d1,d2);
901 else ndirlen+=sprintf(newdir+ndirlen,"/%02d",d1);
902
903 sprintf(olddir,"%s%s",outdir,entry);
904 if (rename(olddir,newdir)) {
905 debuga(__FILE__,__LINE__,_("Error renaming \"%s\" to \"%s\": %s\n"),olddir,newdir,strerror(errno));
906 exit(EXIT_FAILURE);
907 }
908
909 strcpy(newdir+monthlen,"/images");
910 if(access(newdir, R_OK) != 0) {
911 #ifdef HAVE_SYMLINK
912 char linkdir[MAXLEN];
913
914 sprintf(linkdir,"%simages",outdir);
915 if (symlink(linkdir,newdir)) {
916 debuga(__FILE__,__LINE__,_("Failed to create link \"%s\" to \"%s\": %s\n"),linkdir,newdir,strerror(errno));
917 exit(EXIT_FAILURE);
918 }
919 #else
920 char cmd[MAXLEN];
921 int cstatus;
922
923 sprintf(cmd,"ln -s \"%simages\" \"%s/images\"",outdir,newdir);
924 cstatus=system(cmd);
925 if (!WIFEXITED(cstatus) || WEXITSTATUS(cstatus)) {
926 debuga(__FILE__,__LINE__,_("command return status %d\n"),WEXITSTATUS(cstatus));
927 debuga(__FILE__,__LINE__,_("command: %s\n"),cmd);
928 exit(EXIT_FAILURE);
929 }
930 #endif
931 }
932 }
933
934 static void date_index_to_file_index(const char *entry)
935 {
936 int y1, next;
937 int m1, m2;
938 int d1, d2;
939 int val1len;
940 int i, j;
941 char val1[MAXLEN];
942 const char *sm1, *sm2;
943 char *str;
944 char newdir[MAXLEN], olddir[MAXLEN];
945 DIR *dirp2, *dirp3;
946 struct dirent *direntp2;
947 struct dirent *direntp3;
948
949 if(strlen(entry) != 4) return;
950
951 next=-1;
952 if (sscanf(entry,"%d%n",&y1,&next)!=1 || next<0 || entry[next]) return;
953
954 val1len=snprintf(val1,sizeof(val1),"%s%s",outdir,entry);
955 dirp2 = opendir(val1);
956 if (!dirp2) return;
957 while ((direntp2 = readdir( dirp2 )) != NULL) {
958 if(!isdigit(direntp2->d_name[0]) || !isdigit(direntp2->d_name[1])) continue;
959 i=0;
960 str=direntp2->d_name;
961 m1=0;
962 for (j=0 ; j<2 && str[i] && isdigit(str[i]) ; j++)
963 m1=(m1*10)+(str[i++]-'0');
964 if (j>=2) continue;
965 sm1=conv_month_name(m1);
966 if (str[i]=='-') {
967 i++;
968 m2=0;
969 for (j=0 ; j<2 && str[i] && isdigit(str[i]) ; j++)
970 m2=(m2*10)+(str[i++]-'0');
971 if (j>=2) continue;
972 sm2=conv_month_name(m2);
973 } else if (!str[i]) {
974 sm2=sm1;
975 } else {
976 continue;
977 }
978
979 sprintf(val1+val1len,"/%s",direntp2->d_name);
980 dirp3 = opendir(val1);
981 if (!dirp3) continue;
982 while ((direntp3 = readdir( dirp3 )) != NULL) {
983 if(!isdigit(direntp3->d_name[0]) || !isdigit(direntp3->d_name[1])) continue;
984 i=0;
985 str=direntp3->d_name;
986 d1=0;
987 for (j=0 ; str[i] && isdigit(str[i]) ; j++)
988 d1=d1*10+(str[i++]-'0');
989 if (j!=2) continue;
990 if (str[i]=='-') {
991 i++;
992 d2=0;
993 for (j=0 ; str[i] && isdigit(str[i]) ; j++)
994 d2=d2*10+(str[i++]-'0');
995 if (j!=2) continue;
996 } else if (!str[i]) {
997 d2=d1;
998 } else {
999 continue;
1000 }
1001
1002 if (df=='u') sprintf(newdir,"%s%04d%s%02d-%04d%s%02d",outdir,y1,sm1,d1,y1,sm2,d2);
1003 else if (df=='e') sprintf(newdir,"%s%02d%s%04d-%02d%s%04d",outdir,d1,sm1,y1,d2,sm2,y1);
1004 else continue;
1005 sprintf(olddir,"%s%04d/%s/%s",outdir,y1,direntp2->d_name,direntp3->d_name);
1006 if(rename(olddir,newdir)) {
1007 debuga(__FILE__,__LINE__,_("Error renaming \"%s\" to \"%s\": %s\n"),olddir,newdir,strerror(errno));
1008 exit(EXIT_FAILURE);
1009 }
1010 }
1011 closedir( dirp3 );
1012 }
1013 closedir( dirp2 );
1014
1015 /*!
1016 \bug The links to the images in the reports are broken after moving the directories
1017 as the the HTML files are not at the right level for the images any more.
1018 */
1019 }
1020