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