]> git.ipfire.org Git - thirdparty/sarg.git/blame - index.c
mkdir is really a mess on MinGW
[thirdparty/sarg.git] / index.c
CommitLineData
25697a35 1/*
94ff9470 2 * SARG Squid Analysis Report Generator http://sarg.sourceforge.net
67302a9e 3 * 1998, 2013
25697a35
GS
4 *
5 * SARG donations:
6 * please look at http://sarg.sourceforge.net/donations.php
1164c474
FM
7 * Support:
8 * http://sourceforge.net/projects/sarg/forums/forum/363374
25697a35
GS
9 * ---------------------------------------------------------------------
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
24 *
25 */
26
27#include "include/conf.h"
5f3cfd1d 28#include "include/defs.h"
25697a35 29
9afddeae
FM
30#ifdef HAVE_LSTAT
31#define MY_LSTAT lstat
32#else
33#define MY_LSTAT stat
34#endif
35
36
b25b96fe
FM
37static void make_date_index(void);
38static void make_file_index(void);
06ced858
FM
39static void file_index_to_date_index(const char *entry);
40static void date_index_to_file_index(const char *entry);
41
32e71fa4 42void make_index(void)
25697a35 43{
9bd92830
FM
44 DIR *dirp;
45 struct dirent *direntp;
46 char wdir[MAXLEN];
47
48 if(LastLog > 0) mklastlog(outdir);
49
50 if(Index == INDEX_NO) {
a2d39aed 51 sprintf(wdir,"%s"INDEX_HTML_FILE,outdir);
08f9b029
FM
52 if(access(wdir, R_OK) == 0) {
53 if (unlink(wdir)) {
7c4264ec 54 debuga(_("Cannot delete \"%s\": %s\n"),wdir,strerror(errno));
08f9b029
FM
55 exit(EXIT_FAILURE);
56 }
57 }
9bd92830
FM
58 return;
59 }
60
56b97279
FM
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 }
9bd92830
FM
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 }
b25b96fe 86}
25697a35 87
9afddeae
FM
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 */
108static 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 */
133static 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 */
223static 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 */
353static 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 */
379static 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 */
b25b96fe
FM
517static void make_date_index(void)
518{
9afddeae
FM
519 FILE *fp_ou;
520 DIR *dirp;
9bd92830 521 struct dirent *direntp;
9bd92830
FM
522 char yearindex[MAXLEN];
523 char yeardir[MAXLEN];
524 char yearnum[10];
9bd92830
FM
525 int yearsort[150];
526 int nyears;
527 int year;
9afddeae 528 int i, y;
9bd92830 529 int order;
9afddeae
FM
530 int yeardirlen;
531 long long int total_size;
9bd92830
FM
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) {
9afddeae 539 if (!isdigit(direntp->d_name[0]) || !isdigit(direntp->d_name[1]) ||
007905af 540 !isdigit(direntp->d_name[2]) || !isdigit(direntp->d_name[3])) continue;
9afddeae
FM
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 }
9bd92830
FM
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
9afddeae
FM
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 }
9bd92830 580 if((fp_ou=fopen(yearindex,"w"))==NULL) {
d6f0349d 581 debuga(_("(index) Cannot open file %s: %s\n"),yearindex,strerror(errno));
9bd92830
FM
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);
9aaa3361
FM
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);
9afddeae
FM
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
9bd92830
FM
599 for (y=0 ; y<nyears ; y++) {
600 if (order>0)
601 year=yearsort[y];
602 else
603 year=yearsort[nyears-1-y];
9afddeae
FM
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
56b97279 611 fprintf(fp_ou,"<tr><td class=\"data2\"><a href=\"%s/%s\">%s</a></td>",yearnum,INDEX_HTML_FILE,yearnum);
9aaa3361 612 if (IndexFields & INDEXFIELDS_DIRSIZE)
9afddeae
FM
613 {
614 char size_str[40];
9bd92830 615
9afddeae
FM
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);
507460ae 619 }
9afddeae 620 fputs("</tr>\n",fp_ou);
9bd92830
FM
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);
507460ae
FM
626 if (fclose(fp_ou)==EOF) {
627 debuga(_("Write error in %s: %s\n"),yearindex,strerror(errno));
628 exit(EXIT_FAILURE);
629 }
b25b96fe
FM
630}
631
632static void make_file_index(void)
633{
9bd92830
FM
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
a2d39aed 659 sprintf(wdir,"%s"INDEX_HTML_FILE,outdir);
9bd92830
FM
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;
15d3cb5c
FM
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 }
9bd92830
FM
677 item=malloc(sizeof(*item));
678 if (!item) {
679 debuga(_("not enough memory to sort the index\n"));
680 exit(EXIT_FAILURE);
681 }
81a022d8 682 if (df=='u') {
9bd92830
FM
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;
9bd92830
FM
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 }
60ec7f09 726 buildymd(day,mon,year,ftime,sizeof(ftime));
9bd92830
FM
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 }
a87d4d11 734 safe_strcpy(item->date,data,sizeof(item->date));
9bd92830
FM
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) {
d6f0349d 758 debuga(_("(index) Cannot open file %s: %s\n"),wdir,strerror(errno));
9bd92830
FM
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);
a73c9448
FM
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",
767 _("FILE/PERIOD"),_("CREATION DATE"),_("USERS"),_("BYTES"),_("AVERAGE"));
9bd92830
FM
768 for (i=0 ; i<nsort ; i++) {
769 if (order>0)
770 item=sortlist[i];
771 else
772 item=sortlist[nsort-i-1];
773 tuser=obtuser(outdir,item->dirname);
774 obttotal(outdir,item->dirname,tuser,&tbytes,&media);
775 fputs("<tr><td class=\"data2\"",fp_ou);
776 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%d\"",item->sortnum);
777 fprintf(fp_ou,"><a href='%s/%s'>%s</a></td>",item->dirname,ReplaceIndex,item->dirname);
778 fputs("<td class=\"data2\"",fp_ou);
779 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%s\"",item->creationdate);
780 fprintf(fp_ou,">%s</td>",item->date);
781 fprintf(fp_ou,"<td class=\"data\">%d</td>",tuser);
782 fputs("<td class=\"data\"",fp_ou);
783 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%"PRId64"\"",(int64_t)tbytes);
784 fprintf(fp_ou,">%s</td>",fixnum(tbytes,1));
785 fputs("<td class=\"data\"",fp_ou);
786 if (SortTableJs[0]) fprintf(fp_ou," sorttable_customkey=\"%"PRId64"\"",(int64_t)media);
787 fprintf(fp_ou,">%s</td></tr>\n",fixnum(media,1));
788 }
789 fputs("</table></div>\n",fp_ou);
790 if (write_html_trailer(fp_ou)<0)
791 debuga(_("Write error in the index %s\n"),wdir);
792 if (fclose(fp_ou)==EOF)
793 debuga(_("Failed to close the index file %s - %s\n"),wdir,strerror(errno));
794
795 if (sortlist) {
796 for (i=0 ; i<nsort ; i++) {
797 free(sortlist[i]->dirname);
798 free(sortlist[i]);
799 }
800 free(sortlist);
801 }
25697a35 802}
06ced858
FM
803
804static void file_index_to_date_index(const char *entry)
805{
9bd92830
FM
806 int y1, y2, m1, m2, d1, d2;
807 int i, j;
808 int ndirlen;
809 int monthlen;
810 char sm1[8], sm2[8];
811 char olddir[MAXLEN], newdir[MAXLEN];
812
813 if(strlen(entry) < 19) return;
814
815 y1=0;
816 y2=0;
817 memset(sm1,0,sizeof(sm1));
818 memset(sm2,0,sizeof(sm2));
819 d1=0;
820 d2=0;
821 i=0;
81a022d8 822 if (df=='u') {
9bd92830
FM
823 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
824 y1=y1*10+(entry[i++]-'0');
825 if (j!=4) return;
826 for (j=0 ; j<sizeof(sm1)-1 && entry[i] && isalpha(entry[i]) ; j++)
827 sm1[j]=entry[i++];
828 if (j!=3) return;
829 sm1[j]='\0';
830 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
831 d1=d1*10+(entry[i++]-'0');
832 if (j!=2) return;
833
834 if (entry[i++]!='-') return;
835
836 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
837 y2=y2*10+(entry[i++]-'0');
838 if (j!=4) return;
839 for (j=0 ; j<sizeof(sm2)-1 && entry[i] && isalpha(entry[i]) ; j++)
840 sm2[j]=entry[i++];
841 if (j!=3) return;
842 sm2[j]='\0';
843 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
844 d2=d2*10+(entry[i++]-'0');
845 if (j!=2) return;
81a022d8 846 } else if (df=='e') {
9bd92830
FM
847 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
848 d1=d1*10+(entry[i++]-'0');
849 if (j!=2) return;
850 for (j=0 ; j<sizeof(sm1)-1 && entry[i] && isalpha(entry[i]) ; j++)
851 sm1[j]=entry[i++];
852 if (j!=3) return;
853 sm1[j]='\0';
854 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
855 y1=y1*10+(entry[i++]-'0');
856 if (j!=4) return;
857
858 if (entry[i++]!='-') return;
859
860 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
861 d2=d2*10+(entry[i++]-'0');
862 if (j!=2) return;
863 for (j=0 ; j<sizeof(sm2)-1 && entry[i] && isalpha(entry[i]) ; j++)
864 sm2[j]=entry[i++];
865 if (j!=3) return;
866 sm2[j]='\0';
867 for (j=0 ; entry[i] && isdigit(entry[i]) ; j++)
868 y2=y2*10+(entry[i++]-'0');
869 if (j!=4) return;
870 } else
871 return;
872
873 m1=conv_month(sm1);
874 m2=conv_month(sm2);
875 ndirlen=sprintf(newdir,"%s%04d",outdir,y1);
affa72c5 876 if (access(newdir, R_OK) != 0) {
7a9d0965 877 if (PortableMkDir(newdir,0755)) {
affa72c5
FM
878 debuga(_("Cannot create directory %s - %s\n"),newdir,strerror(errno));
879 exit(EXIT_FAILURE);
880 }
881 }
9bd92830
FM
882 if(m1 != m2) ndirlen+=sprintf(newdir+ndirlen,"/%02d-%02d",m1,m2);
883 else ndirlen+=sprintf(newdir+ndirlen,"/%02d",m1);
affa72c5 884 if (access(newdir, R_OK) != 0) {
7a9d0965 885 if (PortableMkDir(newdir,0755)) {
affa72c5
FM
886 debuga(_("Cannot create directory %s - %s\n"),newdir,strerror(errno));
887 exit(EXIT_FAILURE);
888 }
889 }
9bd92830
FM
890 monthlen=ndirlen;
891 if(d1!=d2) ndirlen+=sprintf(newdir+ndirlen,"/%02d-%02d",d1,d2);
892 else ndirlen+=sprintf(newdir+ndirlen,"/%02d",d1);
893
894 sprintf(olddir,"%s%s",outdir,entry);
895 if (rename(olddir,newdir)) {
896 debuga(_("(index) rename error from \"%s\" to \"%s\" - %s\n"),olddir,newdir,strerror(errno));
897 exit(EXIT_FAILURE);
898 }
899
900 strcpy(newdir+monthlen,"/images");
901 if(access(newdir, R_OK) != 0) {
b5f13803 902#ifdef HAVE_SYMLINK
9bd92830 903 char linkdir[MAXLEN];
b5f13803 904
9bd92830
FM
905 sprintf(linkdir,"%simages",outdir);
906 if (symlink(linkdir,newdir)) {
907 debuga(_("failed to create link \"%s\" to \"%s\" - %s\n"),linkdir,newdir,strerror(errno));
908 exit(EXIT_FAILURE);
909 }
b5f13803 910#else
9bd92830
FM
911 char cmd[MAXLEN];
912 int cstatus;
913
914 sprintf(cmd,"ln -s \"%simages\" \"%s/images\"",outdir,newdir);
915 cstatus=system(cmd);
916 if (!WIFEXITED(cstatus) || WEXITSTATUS(cstatus)) {
917 debuga(_("command return status %d\n"),WEXITSTATUS(cstatus));
918 debuga(_("command: %s\n"),cmd);
919 exit(EXIT_FAILURE);
920 }
b5f13803 921#endif
9bd92830 922 }
06ced858
FM
923}
924
925static void date_index_to_file_index(const char *entry)
926{
9bd92830
FM
927 int y1, next;
928 int m1, m2;
929 int d1, d2;
930 int val1len;
931 int i, j;
932 char val1[MAXLEN];
933 const char *sm1, *sm2;
934 char *str;
935 char newdir[MAXLEN], olddir[MAXLEN];
936 DIR *dirp2, *dirp3;
937 struct dirent *direntp2;
938 struct dirent *direntp3;
939
940 if(strlen(entry) != 4) return;
941
942 next=-1;
943 if (sscanf(entry,"%d%n",&y1,&next)!=1 || next<0 || entry[next]) return;
944
945 val1len=snprintf(val1,sizeof(val1),"%s%s",outdir,entry);
946 dirp2 = opendir(val1);
947 if (!dirp2) return;
948 while ((direntp2 = readdir( dirp2 )) != NULL) {
949 if(!isdigit(direntp2->d_name[0]) || !isdigit(direntp2->d_name[1])) continue;
950 i=0;
951 str=direntp2->d_name;
952 m1=0;
953 for (j=0 ; j<2 && str[i] && isdigit(str[i]) ; j++)
954 m1=(m1*10)+(str[i++]-'0');
955 if (j>=2) continue;
956 sm1=conv_month_name(m1);
957 if (str[i]=='-') {
958 i++;
959 m2=0;
960 for (j=0 ; j<2 && str[i] && isdigit(str[i]) ; j++)
961 m2=(m2*10)+(str[i++]-'0');
962 if (j>=2) continue;
963 sm2=conv_month_name(m2);
964 } else if (!str[i]) {
965 sm2=sm1;
966 } else {
967 continue;
968 }
969
970 sprintf(val1+val1len,"/%s",direntp2->d_name);
971 dirp3 = opendir(val1);
972 if (!dirp3) continue;
973 while ((direntp3 = readdir( dirp3 )) != NULL) {
974 if(!isdigit(direntp3->d_name[0]) || !isdigit(direntp3->d_name[1])) continue;
975 i=0;
976 str=direntp3->d_name;
977 d1=0;
978 for (j=0 ; str[i] && isdigit(str[i]) ; j++)
979 d1=d1*10+(str[i++]-'0');
980 if (j!=2) continue;
981 if (str[i]=='-') {
982 i++;
983 d2=0;
984 for (j=0 ; str[i] && isdigit(str[i]) ; j++)
985 d2=d2*10+(str[i++]-'0');
986 if (j!=2) continue;
987 } else if (!str[i]) {
988 d2=d1;
989 } else {
990 continue;
991 }
992
81a022d8
FM
993 if (df=='u') sprintf(newdir,"%s%04d%s%02d-%04d%s%02d",outdir,y1,sm1,d1,y1,sm2,d2);
994 else if (df=='e') sprintf(newdir,"%s%02d%s%04d-%02d%s%04d",outdir,d1,sm1,y1,d2,sm2,y1);
9bd92830
FM
995 else continue;
996 sprintf(olddir,"%s%04d/%s/%s",outdir,y1,direntp2->d_name,direntp3->d_name);
997 if(rename(olddir,newdir)) {
998 debuga(_("(index) rename error from \"%s\" to \"%s\" - %s\n"),olddir,newdir,strerror(errno));
999 exit(EXIT_FAILURE);
1000 }
1001 }
1002 closedir( dirp3 );
1003 }
1004 closedir( dirp2 );
1005
1006 /*!
1007 \bug The links to the images in the reports are broken after moving the directories
1008 as the the HTML files are not at the right level for the images any more.
1009 */
06ced858
FM
1010}
1011