]> git.ipfire.org Git - thirdparty/sarg.git/blob - index.c
More messages merged to reduce the number of messages to translate.
[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(_("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(_("Path too long: "));
152 debuga_more("%s\n",path);
153 exit(EXIT_FAILURE);
154 }
155 if ((dirp=opendir(path))==NULL) {
156 debuga(_("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(_("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(_("Failed to get the file statistics of %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(_("Not enough memory to recurse into subdirectory\n"));
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(_("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(_("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(_("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(_("Failed to get the file statistics of %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(_("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(_("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 debuga(_("Write error in %s/%s: %s\n"),monthdir,INDEX_HTML_FILE,strerror(errno));
348 exit(EXIT_FAILURE);
349 }
350 return(total_size);
351 }
352
353 /*!
354 * Get the name of a month based on its number.
355 *
356 * \param month The month number starting from one.
357 * \param month_name The buffer to store the month name.
358 * \param month_size The size of the \a month_name buffer.
359 */
360 static void name_month(int month,char *month_name,int month_size)
361 {
362 const char *m[12]={N_("January"),N_("February"),N_("March"),N_("April"),N_("May"),N_("June"),N_("July"),
363 N_("August"),N_("September"),N_("October"),N_("November"),N_("December")};
364
365 if (month<1 || month>12) {
366 debuga(_("The internal list of month names is invalid. Please report this bug to the translator.\n"));
367 exit(EXIT_FAILURE);
368 }
369 strncpy(month_name,_(m[month-1]),month_size-1);
370 month_name[month_size-1]='\0';
371 }
372
373 /*!
374 * Rebuild the html index file for a month when the reports are grouped in a date tree.
375 *
376 * \param yeardir The buffer containing the path where the html index file must be rebuild.
377 * The buffer must be big enough to contain the deepest path in that directory as the buffer is
378 * used to concatenate the directory entries.
379 * \param yeardir_size The size, in byte, of the \a yeardir buffer.
380 * \param order A postive number to sort the index file in positive order. A negative value sort it
381 * in decreasing order.
382 * \param yearnum The string naming the year in the date tree.
383 *
384 * \return The approximate size occupied by the directory.
385 */
386 static long long int make_date_index_month(char *yeardir,int yeardir_size,int order,const char *yearnum)
387 {
388 int yeardir_len;
389 int nmonths;
390 DIR *dirp2;
391 struct dirent *direntp;
392 struct stat statb;
393 int i;
394 int monthsort[144];
395 int m1, m2, month;
396 FILE *fp_ou;
397 char title[80];
398 char monthname1[9], monthname2[9];
399 char nmonth[30];
400 char monthnum[10];
401 int m;
402 long long int total_size=0;
403 long long int sub_size;
404 int name_len;
405
406 nmonths=0;
407 if ((dirp2 = opendir(yeardir)) == NULL) {
408 debuga(_("Cannot open directory \"%s\": %s\n"),yeardir,strerror(errno));
409 exit(EXIT_FAILURE);
410 }
411 yeardir_len=strlen(yeardir);
412 if (yeardir_len+strlen(INDEX_HTML_FILE)+2>=yeardir_size) {
413 debuga(_("Path too long: "));
414 debuga_more("%s/%s\n",yeardir,INDEX_HTML_FILE);
415 exit(EXIT_FAILURE);
416 }
417 yeardir[yeardir_len++]='/';
418 while ((direntp = readdir( dirp2 )) != NULL) {
419 if (direntp->d_name[0]=='.' && (direntp->d_name[1]=='\0' || (direntp->d_name[1]=='.' && direntp->d_name[2]=='\0'))) continue;
420 name_len=strlen(direntp->d_name);
421 if (yeardir_len+name_len+1>=yeardir_size) {
422 debuga(_("Path too long: "));
423 debuga_more("%s%s\n",yeardir,direntp->d_name);
424 exit(EXIT_FAILURE);
425 }
426 strcpy(yeardir+yeardir_len,direntp->d_name);
427 if (MY_LSTAT(yeardir,&statb) == -1) {
428 debuga(_("Failed to get the file statistics of %s: %s\n"),yeardir,strerror(errno));
429 continue;
430 }
431 if (S_ISDIR(statb.st_mode))
432 {
433 if(!isdigit(direntp->d_name[0]) || !isdigit(direntp->d_name[1])) continue;
434 i=-1;
435 if (sscanf(direntp->d_name,"%d%n",&m1,&i)!=1 || m1<1 || m1>12 || i<0) continue;
436 if (direntp->d_name[i]=='-') {
437 if (sscanf(direntp->d_name+i+1,"%d",&m2)!=1 || m2<1 || m2>12) continue;
438 } else if (direntp->d_name[i]!='\0') {
439 continue;
440 } else {
441 m2=0;
442 }
443 if (nmonths>=sizeof(monthsort)/sizeof(monthsort[0])) {
444 debuga(_("Too many month directories in %s\nSupernumerary entries are ignored\n"),yeardir);
445 break;
446 }
447 month=(m1<<4) | m2;
448 for (i=nmonths ; i>0 && month<monthsort[i-1] ; i--) {
449 monthsort[i]=monthsort[i-1];
450 }
451 monthsort[i]=month;
452 nmonths++;
453 total_size+=get_file_size(&statb);
454 }
455 else if (S_ISREG(statb.st_mode))
456 {
457 total_size+=get_file_size(&statb);
458 }
459 }
460 closedir(dirp2);
461
462 strcpy(yeardir+yeardir_len,INDEX_HTML_FILE);
463 if((fp_ou=fopen(yeardir,"w"))==NULL) {
464 debuga(_("Cannot open file \"%s\": %s\n"),yeardir,strerror(errno));
465 exit(EXIT_FAILURE);
466 }
467 snprintf(title,sizeof(title),ngettext("SARG: report for %s","SARG: reports for %s",nmonths),yearnum);
468 write_html_header(fp_ou,1,title,HTML_JS_NONE);
469 close_html_header(fp_ou);
470 fputs("<div class=\"index\"><table cellpadding=\"1\" cellspacing=\"2\">\n<tr><td></td><td></td></tr>\n",fp_ou);
471 fprintf(fp_ou,"<tr><th class=\"header_l\">%s/%s</th>",_("YEAR"),_("MONTH"));
472 if (IndexFields & INDEXFIELDS_DIRSIZE)
473 fprintf(fp_ou,"<th class=\"header_l\">%s</th>",_("SIZE"));
474 fputs("</tr>\n",fp_ou);
475 for (m=0 ; m<nmonths ; m++) {
476 if (order>0)
477 month=monthsort[m];
478 else
479 month=monthsort[nmonths-1-m];
480 m1=(month >> 4) & 0x0F;
481 if ((month & 0x0F) != 0) {
482 m2=month & 0x0F;
483 sprintf(monthnum,"%02d-%02d",m1,m2);
484 name_month(m1,monthname1,sizeof(monthname1));
485 name_month(m2,monthname2,sizeof(monthname2));
486 sprintf(nmonth,"%s-%s",monthname1,monthname2);
487 } else {
488 sprintf(monthnum,"%02d",m1);
489 name_month(m1,nmonth,sizeof(nmonth));
490 }
491 if (yeardir_len+strlen(monthnum)+1>=yeardir_size) {
492 yeardir[yeardir_len]='\0';
493 debuga(_("Path too long: "));
494 debuga_more("%s%s\n",yeardir,monthnum);
495 exit(EXIT_FAILURE);
496 }
497 strcpy(yeardir+yeardir_len,monthnum);
498 sub_size=make_date_index_day(yeardir,yeardir_size,order,yearnum,nmonth);
499
500 fprintf(fp_ou,"<tr><td class=\"data2\"><a href=\"%s/%s\">%s %s</a></td>",monthnum,INDEX_HTML_FILE,yearnum,nmonth);
501 if (IndexFields & INDEXFIELDS_DIRSIZE)
502 {
503 char size_str[40];
504
505 strncpy(size_str,fixnum(sub_size,1),sizeof(size_str)-1);
506 size_str[sizeof(size_str)-1]='\0';
507 fprintf(fp_ou,"<td class=\"data2\">%s</td>",size_str);
508 }
509 fputs("</tr>\n",fp_ou);
510 total_size+=sub_size;
511 }
512 fputs("</table></div>\n",fp_ou);
513 yeardir[yeardir_len-1]='\0';
514 write_html_trailer(fp_ou);
515 if (fclose(fp_ou)==EOF) {
516 debuga(_("Write error in %s/%s: %s\n"),yeardir,INDEX_HTML_FILE,strerror(errno));
517 exit(EXIT_FAILURE);
518 }
519 return(total_size);
520 }
521
522 /*!
523 * Rebuild a date index tree in the output directory.
524 */
525 static void make_date_index(void)
526 {
527 FILE *fp_ou;
528 DIR *dirp;
529 struct dirent *direntp;
530 char yearindex[MAXLEN];
531 char yeardir[MAXLEN];
532 char yearnum[10];
533 int yearsort[150];
534 int nyears;
535 int year;
536 int i, y;
537 int order;
538 int yeardirlen;
539 long long int total_size;
540
541 nyears=0;
542 if ((dirp = opendir(outdir)) == NULL) {
543 debuga(_("Cannot open directory \"%s\": %s\n"),outdir,strerror(errno));
544 exit(EXIT_FAILURE);
545 }
546 while ((direntp = readdir( dirp )) != NULL) {
547 if (!isdigit(direntp->d_name[0]) || !isdigit(direntp->d_name[1]) ||
548 !isdigit(direntp->d_name[2]) || !isdigit(direntp->d_name[3])) continue;
549 year=atoi(direntp->d_name) << 10;
550 if (direntp->d_name[4]=='-')
551 {
552 if (!isdigit(direntp->d_name[5]) || !isdigit(direntp->d_name[6]) ||
553 !isdigit(direntp->d_name[7]) || !isdigit(direntp->d_name[8])) continue;
554 if (direntp->d_name[9]) continue;
555 year|=atoi(direntp->d_name+5);
556 }
557 else
558 {
559 if (direntp->d_name[4]) continue;
560 }
561 if (nyears>=sizeof(yearsort)/sizeof(yearsort[0])) {
562 /*
563 If too many years are listed in the directory, we ignore the earliest years. The yearsort array
564 is big enough to accomodate the most ambitious use of sarg but this safety is added to prevent
565 a crash should the directory be polluted by other entries.
566 */
567 if (year>yearsort[0]) {
568 for (i=1 ; i<nyears && year>yearsort[i] ; i++)
569 yearsort[i-1]=yearsort[i];
570 yearsort[i-1]=year;
571 }
572 } else {
573 for (i=nyears ; i>0 && year<yearsort[i-1] ; i--) {
574 yearsort[i]=yearsort[i-1];
575 }
576 yearsort[i]=year;
577 nyears++;
578 }
579 }
580 closedir( dirp );
581
582 order=(strcmp(IndexSortOrder,"A") == 0) ? 1 : -1;
583
584 if (snprintf(yearindex,sizeof(yearindex),"%s"INDEX_HTML_FILE,outdir)>=sizeof(yearindex)) {
585 debuga(_("Resulting index file name too long: %s/%s"),outdir,INDEX_HTML_FILE);
586 exit(EXIT_FAILURE);
587 }
588 if((fp_ou=fopen(yearindex,"w"))==NULL) {
589 debuga(_("Cannot open file \"%s\": %s\n"),yearindex,strerror(errno));
590 exit(EXIT_FAILURE);
591 }
592 write_html_header(fp_ou,0,ngettext("SARG report","SARG reports",nyears),HTML_JS_NONE);
593 close_html_header(fp_ou);
594 fputs("<div class=\"index\"><table cellpadding=\"1\" cellspacing=\"2\">\n",fp_ou);
595 fprintf(fp_ou,"<tr><th class=\"header_l\">%s</th>",_("YEAR"));
596 if (IndexFields & INDEXFIELDS_DIRSIZE)
597 fprintf(fp_ou,"<th class=\"header_l\">%s</th>",_("SIZE"));
598 fputs("</tr>\n",fp_ou);
599
600 yeardirlen=strlen(outdir);
601 if (yeardirlen>=sizeof(yeardir)) {
602 debuga(_("Path too long: "));
603 debuga_more("%s",outdir);
604 exit(EXIT_FAILURE);
605 }
606 strcpy(yeardir,outdir);
607
608 for (y=0 ; y<nyears ; y++) {
609 if (order>0)
610 year=yearsort[y];
611 else
612 year=yearsort[nyears-1-y];
613 if ((year & 0x3FF)==0)
614 sprintf(yearnum,"%04d",year>>10);
615 else
616 sprintf(yearnum,"%04d-%04d",year>>10,year & 0x3FF);
617 strcpy(yeardir+yeardirlen,yearnum);
618 total_size=make_date_index_month(yeardir,sizeof(yeardir),order,yearnum);
619
620 fprintf(fp_ou,"<tr><td class=\"data2\"><a href=\"%s/%s\">%s</a></td>",yearnum,INDEX_HTML_FILE,yearnum);
621 if (IndexFields & INDEXFIELDS_DIRSIZE)
622 {
623 char size_str[40];
624
625 strncpy(size_str,fixnum(total_size,1),sizeof(size_str)-1);
626 size_str[sizeof(size_str)-1]='\0';
627 fprintf(fp_ou,"<td class=\"data2\">%s</td>",size_str);
628 }
629 fputs("</tr>\n",fp_ou);
630 }
631
632 fputs("</table></div>\n",fp_ou);
633 write_html_trailer(fp_ou);
634 if (fclose(fp_ou)==EOF) {
635 debuga(_("Write error in \"%s\": %s\n"),yearindex,strerror(errno));
636 exit(EXIT_FAILURE);
637 }
638 }
639
640 static void make_file_index(void)
641 {
642 #define MAX_CREATION_DATE 15
643 FILE *fp_ou;
644 DIR *dirp;
645 struct dirent *direntp;
646 char wdir[MAXLEN];
647 char data[80];
648 char ftime[128];
649 char day[6], mon[8], year[40], hour[10];
650 long long int tbytes;
651 long long int media;
652 int iyear, imonth, iday, ihour, iminute, isecond, idst;
653 int nsort;
654 int nallocated;
655 int order;
656 int i;
657 int tuser;
658 struct getwordstruct gwarea;
659 struct sortstruct
660 {
661 int year, month, day, sortnum;
662 char creationdate[MAX_CREATION_DATE];
663 char *dirname;
664 char date[60];
665 } **sortlist, *item, **tempsort;
666
667 sprintf(wdir,"%s"INDEX_HTML_FILE,outdir);
668
669 order=(strcmp(IndexSortOrder,"A") == 0) ? 1 : -1;
670
671 if ((dirp = opendir(outdir)) == NULL) {
672 debuga(_("Cannot open directory \"%s\": %s\n"),outdir,strerror(errno));
673 exit(EXIT_FAILURE);
674 }
675
676 nsort=0;
677 nallocated=0;
678 sortlist=NULL;
679 while ((direntp = readdir( dirp )) != NULL) {
680 if (strchr(direntp->d_name,'-') == 0) continue;
681 if (obtdate(outdir,direntp->d_name,data)<0) {
682 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);
683 continue;
684 }
685 item=malloc(sizeof(*item));
686 if (!item) {
687 debuga(_("not enough memory to sort the index\n"));
688 exit(EXIT_FAILURE);
689 }
690 if (df=='u') {
691 item->year=atoi(direntp->d_name);
692 item->month=conv_month(direntp->d_name+4);
693 item->day=atoi(direntp->d_name+7);
694 } else {
695 item->year=atoi(direntp->d_name+5);
696 item->month=conv_month(direntp->d_name+2);
697 item->day=atoi(direntp->d_name);
698 }
699 item->sortnum=(item->year*16+item->month)*32+item->day;
700 if (sscanf(data,"%d-%d-%d %d:%d:%d %d",&iyear,&imonth,&iday,&ihour,&iminute,&isecond,&idst)==7) {
701 formatdate(data,sizeof(data),iyear,imonth,iday,ihour,iminute,isecond,idst);
702 snprintf(item->creationdate,sizeof(item->creationdate),"%04d%02d%02d%02d%02d%02d",iyear,imonth,iday,ihour,iminute,isecond);
703 } else {
704 /*
705 Old code to parse a date stored by sarg before 2.2.6.1 in the sarg-date file of each report directory.
706 */
707 getword_start(&gwarea,data);
708 if (getword_skip(16,&gwarea,' ')<0) {
709 debuga(_("Maybe you have a broken week day in your %s%s/sarg-date file\n"),outdir,direntp->d_name);
710 exit(EXIT_FAILURE);
711 }
712 if (getword_multisep(mon,sizeof(mon),&gwarea,' ')<0) {
713 debuga(_("Maybe you have a broken month in your %s%s/sarg-date file\n"),outdir,direntp->d_name);
714 exit(EXIT_FAILURE);
715 }
716 if (getword_multisep(day,sizeof(day),&gwarea,' ')<0) {
717 debuga(_("Maybe you have a broken day in your %s%s/sarg-date file\n"),outdir,direntp->d_name);
718 exit(EXIT_FAILURE);
719 }
720 if (getword_multisep(hour,sizeof(hour),&gwarea,' ')<0) {
721 debuga(_("Maybe you have a broken time in your %s%s/sarg-date file\n"),outdir,direntp->d_name);
722 exit(EXIT_FAILURE);
723 }
724 do {
725 if (getword_multisep(year,sizeof(year),&gwarea,' ')<0) {
726 debuga(_("Maybe you have a broken year in your %s%s/sarg-date file\n"),outdir,direntp->d_name);
727 exit(EXIT_FAILURE);
728 }
729 } while (year[0] && !isdigit(year[0])); //skip time zone information with spaces until the year is found
730 if (sscanf(hour,"%d:%d:%d",&ihour,&iminute,&isecond)!=3) {
731 debuga(_("Maybe you have a broken time in your %s%s/sarg-date file\n"),outdir,direntp->d_name);
732 exit(EXIT_FAILURE);
733 }
734 buildymd(day,mon,year,ftime,sizeof(ftime));
735 snprintf(item->creationdate,sizeof(item->creationdate),"%s%02d%02d%02d",ftime, ihour, iminute, isecond);
736 }
737 item->dirname=strdup(direntp->d_name);
738 if (!item->dirname) {
739 debuga(_("Not enough memory to store the directory name \"%s\" in the index\n"),direntp->d_name);
740 exit(EXIT_FAILURE);
741 }
742 safe_strcpy(item->date,data,sizeof(item->date));
743 if (nsort+1>nallocated) {
744 nallocated+=10;
745 tempsort=realloc(sortlist,nallocated*sizeof(*item));
746 if (!tempsort) {
747 debuga(_("not enough memory to sort the index\n"));
748 exit(EXIT_FAILURE);
749 }
750 sortlist=tempsort;
751 }
752 for (i=nsort ; i>0 ; i--) {
753 if (item->sortnum>sortlist[i-1]->sortnum) break;
754 if (item->sortnum==sortlist[i-1]->sortnum) {
755 if (strcmp(item->creationdate,sortlist[i-1]->creationdate)>=0) break;
756 }
757 sortlist[i]=sortlist[i-1];
758 }
759 sortlist[i]=item;
760 nsort++;
761 }
762
763 closedir( dirp );
764
765 if((fp_ou=fopen(wdir,"w"))==NULL) {
766 debuga(_("Cannot open file \"%s\": %s\n"),wdir,strerror(errno));
767 exit(EXIT_FAILURE);
768 }
769 write_html_header(fp_ou,0,ngettext("SARG report","SARG reports",nsort),HTML_JS_SORTTABLE);
770 close_html_header(fp_ou);
771 fputs("<div class=\"index\"><table cellpadding=\"1\" cellspacing=\"2\"",fp_ou);
772 if (SortTableJs[0]) fputs(" class=\"sortable\"",fp_ou);
773 fputs(">\n",fp_ou);
774 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",
775 _("FILE/PERIOD"),_("CREATION DATE"),_("USERS"),_("BYTES"),_("AVERAGE"));
776 for (i=0 ; i<nsort ; i++) {
777 if (order>0)
778 item=sortlist[i];
779 else
780 item=sortlist[nsort-i-1];
781 tuser=obtuser(outdir,item->dirname);
782 obttotal(outdir,item->dirname,tuser,&tbytes,&media);
783 fputs("<tr><td class=\"data2\"",fp_ou);
784 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%d\"",item->sortnum);
785 fprintf(fp_ou,"><a href='%s/%s'>%s</a></td>",item->dirname,ReplaceIndex,item->dirname);
786 fputs("<td class=\"data2\"",fp_ou);
787 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%s\"",item->creationdate);
788 fprintf(fp_ou,">%s</td>",item->date);
789 fprintf(fp_ou,"<td class=\"data\">%d</td>",tuser);
790 fputs("<td class=\"data\"",fp_ou);
791 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%"PRId64"\"",(int64_t)tbytes);
792 fprintf(fp_ou,">%s</td>",fixnum(tbytes,1));
793 fputs("<td class=\"data\"",fp_ou);
794 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%"PRId64"\"",(int64_t)media);
795 fprintf(fp_ou,">%s</td></tr>\n",fixnum(media,1));
796 }
797 fputs("</table></div>\n",fp_ou);
798 write_html_trailer(fp_ou);
799 if (fclose(fp_ou)==EOF)
800 debuga(_("Write error in file \"%s\": %s\n"),wdir,strerror(errno));
801
802 if (sortlist) {
803 for (i=0 ; i<nsort ; i++) {
804 free(sortlist[i]->dirname);
805 free(sortlist[i]);
806 }
807 free(sortlist);
808 }
809 }
810
811 static void file_index_to_date_index(const char *entry)
812 {
813 int y1, y2, m1, m2, d1, d2;
814 int i, j;
815 int ndirlen;
816 int monthlen;
817 char sm1[8], sm2[8];
818 char olddir[MAXLEN], newdir[MAXLEN];
819
820 if(strlen(entry) < 19) return;
821
822 y1=0;
823 y2=0;
824 memset(sm1,0,sizeof(sm1));
825 memset(sm2,0,sizeof(sm2));
826 d1=0;
827 d2=0;
828 i=0;
829 if (df=='u') {
830 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
831 y1=y1*10+(entry[i++]-'0');
832 if (j!=4) return;
833 for (j=0 ; j<sizeof(sm1)-1 && entry[i] && isalpha(entry[i]) ; j++)
834 sm1[j]=entry[i++];
835 if (j!=3) return;
836 sm1[j]='\0';
837 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
838 d1=d1*10+(entry[i++]-'0');
839 if (j!=2) return;
840
841 if (entry[i++]!='-') return;
842
843 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
844 y2=y2*10+(entry[i++]-'0');
845 if (j!=4) return;
846 for (j=0 ; j<sizeof(sm2)-1 && entry[i] && isalpha(entry[i]) ; j++)
847 sm2[j]=entry[i++];
848 if (j!=3) return;
849 sm2[j]='\0';
850 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
851 d2=d2*10+(entry[i++]-'0');
852 if (j!=2) return;
853 } else if (df=='e') {
854 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
855 d1=d1*10+(entry[i++]-'0');
856 if (j!=2) return;
857 for (j=0 ; j<sizeof(sm1)-1 && entry[i] && isalpha(entry[i]) ; j++)
858 sm1[j]=entry[i++];
859 if (j!=3) return;
860 sm1[j]='\0';
861 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
862 y1=y1*10+(entry[i++]-'0');
863 if (j!=4) return;
864
865 if (entry[i++]!='-') return;
866
867 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
868 d2=d2*10+(entry[i++]-'0');
869 if (j!=2) return;
870 for (j=0 ; j<sizeof(sm2)-1 && entry[i] && isalpha(entry[i]) ; j++)
871 sm2[j]=entry[i++];
872 if (j!=3) return;
873 sm2[j]='\0';
874 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
875 y2=y2*10+(entry[i++]-'0');
876 if (j!=4) return;
877 } else
878 return;
879
880 m1=conv_month(sm1);
881 m2=conv_month(sm2);
882 ndirlen=sprintf(newdir,"%s%04d",outdir,y1);
883 if (access(newdir, R_OK) != 0) {
884 if (PortableMkDir(newdir,0755)) {
885 debuga(_("Cannot create directory \"%s\": %s\n"),newdir,strerror(errno));
886 exit(EXIT_FAILURE);
887 }
888 }
889 if(m1 != m2) ndirlen+=sprintf(newdir+ndirlen,"/%02d-%02d",m1,m2);
890 else ndirlen+=sprintf(newdir+ndirlen,"/%02d",m1);
891 if (access(newdir, R_OK) != 0) {
892 if (PortableMkDir(newdir,0755)) {
893 debuga(_("Cannot create directory \"%s\": %s\n"),newdir,strerror(errno));
894 exit(EXIT_FAILURE);
895 }
896 }
897 monthlen=ndirlen;
898 if(d1!=d2) ndirlen+=sprintf(newdir+ndirlen,"/%02d-%02d",d1,d2);
899 else ndirlen+=sprintf(newdir+ndirlen,"/%02d",d1);
900
901 sprintf(olddir,"%s%s",outdir,entry);
902 if (rename(olddir,newdir)) {
903 debuga(_("Error renaming \"%s\" to \"%s\": %s\n"),olddir,newdir,strerror(errno));
904 exit(EXIT_FAILURE);
905 }
906
907 strcpy(newdir+monthlen,"/images");
908 if(access(newdir, R_OK) != 0) {
909 #ifdef HAVE_SYMLINK
910 char linkdir[MAXLEN];
911
912 sprintf(linkdir,"%simages",outdir);
913 if (symlink(linkdir,newdir)) {
914 debuga(_("Failed to create link \"%s\" to \"%s\": %s\n"),linkdir,newdir,strerror(errno));
915 exit(EXIT_FAILURE);
916 }
917 #else
918 char cmd[MAXLEN];
919 int cstatus;
920
921 sprintf(cmd,"ln -s \"%simages\" \"%s/images\"",outdir,newdir);
922 cstatus=system(cmd);
923 if (!WIFEXITED(cstatus) || WEXITSTATUS(cstatus)) {
924 debuga(_("command return status %d\n"),WEXITSTATUS(cstatus));
925 debuga(_("command: %s\n"),cmd);
926 exit(EXIT_FAILURE);
927 }
928 #endif
929 }
930 }
931
932 static void date_index_to_file_index(const char *entry)
933 {
934 int y1, next;
935 int m1, m2;
936 int d1, d2;
937 int val1len;
938 int i, j;
939 char val1[MAXLEN];
940 const char *sm1, *sm2;
941 char *str;
942 char newdir[MAXLEN], olddir[MAXLEN];
943 DIR *dirp2, *dirp3;
944 struct dirent *direntp2;
945 struct dirent *direntp3;
946
947 if(strlen(entry) != 4) return;
948
949 next=-1;
950 if (sscanf(entry,"%d%n",&y1,&next)!=1 || next<0 || entry[next]) return;
951
952 val1len=snprintf(val1,sizeof(val1),"%s%s",outdir,entry);
953 dirp2 = opendir(val1);
954 if (!dirp2) return;
955 while ((direntp2 = readdir( dirp2 )) != NULL) {
956 if(!isdigit(direntp2->d_name[0]) || !isdigit(direntp2->d_name[1])) continue;
957 i=0;
958 str=direntp2->d_name;
959 m1=0;
960 for (j=0 ; j<2 && str[i] && isdigit(str[i]) ; j++)
961 m1=(m1*10)+(str[i++]-'0');
962 if (j>=2) continue;
963 sm1=conv_month_name(m1);
964 if (str[i]=='-') {
965 i++;
966 m2=0;
967 for (j=0 ; j<2 && str[i] && isdigit(str[i]) ; j++)
968 m2=(m2*10)+(str[i++]-'0');
969 if (j>=2) continue;
970 sm2=conv_month_name(m2);
971 } else if (!str[i]) {
972 sm2=sm1;
973 } else {
974 continue;
975 }
976
977 sprintf(val1+val1len,"/%s",direntp2->d_name);
978 dirp3 = opendir(val1);
979 if (!dirp3) continue;
980 while ((direntp3 = readdir( dirp3 )) != NULL) {
981 if(!isdigit(direntp3->d_name[0]) || !isdigit(direntp3->d_name[1])) continue;
982 i=0;
983 str=direntp3->d_name;
984 d1=0;
985 for (j=0 ; str[i] && isdigit(str[i]) ; j++)
986 d1=d1*10+(str[i++]-'0');
987 if (j!=2) continue;
988 if (str[i]=='-') {
989 i++;
990 d2=0;
991 for (j=0 ; str[i] && isdigit(str[i]) ; j++)
992 d2=d2*10+(str[i++]-'0');
993 if (j!=2) continue;
994 } else if (!str[i]) {
995 d2=d1;
996 } else {
997 continue;
998 }
999
1000 if (df=='u') sprintf(newdir,"%s%04d%s%02d-%04d%s%02d",outdir,y1,sm1,d1,y1,sm2,d2);
1001 else if (df=='e') sprintf(newdir,"%s%02d%s%04d-%02d%s%04d",outdir,d1,sm1,y1,d2,sm2,y1);
1002 else continue;
1003 sprintf(olddir,"%s%04d/%s/%s",outdir,y1,direntp2->d_name,direntp3->d_name);
1004 if(rename(olddir,newdir)) {
1005 debuga(_("Error renaming \"%s\" to \"%s\": %s\n"),olddir,newdir,strerror(errno));
1006 exit(EXIT_FAILURE);
1007 }
1008 }
1009 closedir( dirp3 );
1010 }
1011 closedir( dirp2 );
1012
1013 /*!
1014 \bug The links to the images in the reports are broken after moving the directories
1015 as the the HTML files are not at the right level for the images any more.
1016 */
1017 }
1018