1 /*
2     graphs.c  - produces graphs used by the Webalizer
3 
4     Copyright (C) 1997-2013  Bradford L. Barrett
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version, and provided that the above
10     copyright and permission notice is included with all distributed
11     copies of this or derived software.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 
22 */
23 
24 #include <math.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <errno.h>
29 #include <sys/stat.h>
30 #include <gd.h>
31 #include <gdfontt.h>
32 #include <gdfonts.h>
33 #include <gdfontmb.h>
34 
35 /* need socket header? */
36 #ifdef HAVE_SYS_SOCKET_H
37 #include <sys/socket.h>
38 #endif
39 
40 #include "webalizer.h"
41 #include "preserve.h"
42 #include "lang.h"
43 #include "graphs.h"
44 
45 /* Some systems don't define this */
46 #ifndef PI
47 #define PI 3.14159265358979323846
48 #endif
49 
50 #define HITCOLOR   hit_or_green            /* graph color - hits  */
51 #define FILECOLOR  file_or_blue            /* files               */
52 #define SITECOLOR  site_or_orange          /* sites               */
53 #define KBYTECOLOR kbyte_or_red            /* KBytes              */
54 #define PAGECOLOR  page_or_cyan            /* Files               */
55 #define VISITCOLOR visit_or_yellow         /* Visits              */
56 
57 /* shortcuts to convert ASCII hex color for gdImageColorAllocate() */
58 
59 #define getred(s) (ashex2int((s[0] == '#')?s+1:s))
60 /* returns the red base-10 integer value from a html color */
61 
62 #define getgreen(s) (ashex2int((s[0] == '#')?s+3:s+2))
63 /* returns the green base-10 integer value from a html color */
64 
65 #define getblue(s) (ashex2int((s[0] == '#')?s+5:s+4))
66 /* returns the blue base-10 integer value from a html color */
67 
68 #define CX 156                             /* center x (for pie)  */
69 #define CY 150                             /* center y  (chart)   */
70 #define XRAD 240                           /* X-axis radius       */
71 #define YRAD 200                           /* Y-axis radius       */
72 
73 /* forward reference internal routines */
74 
75 void    init_graph(char *, int, int);
76 struct  pie_data *calc_arc(float, float);
77 int     ashex2int(char *);
78 
79 /* common public declarations */
80 
81 char *numchar[] = { " 0"," 1"," 2"," 3"," 4"," 5"," 6"," 7"," 8"," 9","10",
82                     "11","12","13","14","15","16","17","18","19","20",
83                     "21","22","23","24","25","26","27","28","29","30","31"};
84 
85 gdImagePtr      im;                        /* image buffer        */
86 FILE            *out;                      /* output file for PNG */
87 struct stat     out_stat;                  /* stat struct for PNG */
88 char            maxvaltxt[32];             /* graph values        */
89 float           percent;                   /* percent storage     */
90 u_int64_t       julday;                    /* julday value        */
91 
92 struct pie_data { int x; int y;            /* line x,y            */
93                   int mx; int my; };       /* midpoint x,y        */
94 /* colors */
95 int             black, white, grey, dkgrey, kbyte_or_red,
96                 file_or_blue, site_or_orange, hit_or_green,
97                 page_or_cyan, visit_or_yellow, blue;
98 
99 /*****************************************************************/
100 /*                                                               */
101 /* YEAR_GRAPH6x  - Year graph from array of hist_rec structs     */
102 /*                                                               */
103 /*****************************************************************/
104 
year_graph6x(char * fname,char * title,struct hist_rec data[HISTSIZE])105 int year_graph6x(char *fname, char *title, struct hist_rec data[HISTSIZE])
106 {
107 
108    /* local variables */
109    int i,j,x1,y1,x2;
110    int s_mth,s_year=0;
111    float cw,cs,co,ci;
112 
113    u_int64_t  maxval=1;
114    double     fmaxval=0.0;
115 
116    /* initalize the graph */
117    init_graph(title,512,256);              /* init as 512 x 256  */
118 
119    gdImageLine(im, 305,25,305,233,black);  /* draw section lines */
120    gdImageLine(im, 304,25,304,233,white);
121    gdImageLine(im, 305,130,490,130,black);
122    gdImageLine(im, 305,129,490,129,white);
123 
124    /* index lines? */
125    if (graph_lines)
126    {
127       y1=210/(graph_lines+1);
128       for (i=0;i<graph_lines;i++)
129        gdImageLine(im,21,((i+1)*y1)+25,303,((i+1)*y1)+25,dkgrey);
130       y1=105/(graph_lines+1);
131       for (i=0;i<graph_lines;i++)
132        gdImageLine(im,306,((i+1)*y1)+25,489,((i+1)*y1)+25,dkgrey);
133       for (i=0;i<graph_lines;i++)
134        gdImageLine(im,306,((i+1)*y1)+130,489,((i+1)*y1)+130,dkgrey);
135    }
136 
137    /* color coded legends? */
138    if (graph_legend)
139    {
140       /* Kbytes Legend */
141       i = (strlen(msg_h_xfer)*6);
142       gdImageString(im,gdFontSmall,491-i,237,
143                     (unsigned char *)msg_h_xfer,dkgrey);
144       gdImageString(im,gdFontSmall,490-i,236,
145                     (unsigned char *)msg_h_xfer,KBYTECOLOR);
146 
147       /* Sites/Visits Legend */
148       i = (strlen(msg_h_visits)*6);
149       j = (strlen(msg_h_sites)*6);
150       gdImageString(im,gdFontSmall,491-i-j-12,11,
151                     (unsigned char *)msg_h_visits,dkgrey);
152       gdImageString(im,gdFontSmall,490-i-j-12,10,
153                     (unsigned char *)msg_h_visits,VISITCOLOR);
154       gdImageString(im,gdFontSmall,491-j-9,11,(unsigned char *)"/",dkgrey);
155       gdImageString(im,gdFontSmall,490-j-9,10,(unsigned char *)"/",black);
156       gdImageString(im,gdFontSmall,491-j,11,
157                     (unsigned char *)msg_h_sites,dkgrey);
158       gdImageString(im,gdFontSmall,490-j,10,
159                     (unsigned char *)msg_h_sites,SITECOLOR);
160 
161       /* Hits/Files/Pages Legend */
162       i = (strlen(msg_h_pages)*6);
163       j = (strlen(msg_h_files)*6);
164       gdImageStringUp(im,gdFontSmall,6,231,
165                       (unsigned char *)msg_h_pages,dkgrey);
166       gdImageStringUp(im,gdFontSmall,5,230,
167                       (unsigned char *)msg_h_pages,PAGECOLOR);
168       gdImageStringUp(im,gdFontSmall,6,231-i-3,(unsigned char *)"/",dkgrey);
169       gdImageStringUp(im,gdFontSmall,5,230-i-3,(unsigned char *)"/",black);
170       gdImageStringUp(im,gdFontSmall,6,231-i-12,
171                       (unsigned char *)msg_h_files,dkgrey);
172       gdImageStringUp(im,gdFontSmall,5,230-i-12,
173                       (unsigned char *)msg_h_files,FILECOLOR);
174       gdImageStringUp(im,gdFontSmall,6,231-i-j-15,(unsigned char *)"/",dkgrey);
175       gdImageStringUp(im,gdFontSmall,5,230-i-j-15,(unsigned char *)"/",black);
176       gdImageStringUp(im,gdFontSmall,6,231-i-j-24,
177                       (unsigned char *)msg_h_hits,dkgrey);
178       gdImageStringUp(im,gdFontSmall,5,230-i-j-24,
179                       (unsigned char *)msg_h_hits,HITCOLOR);
180    }
181 
182    /* Now draw data areas */
183    s_mth = HISTSIZE-graph_mths;
184    cs = 280.0/graph_mths; cw = cs/2;
185    co = (48/graph_mths<1)?1:48/graph_mths;
186    ci = 22+((cw-co)/2);
187 
188    /* x-axis legend */
189    for (i=s_mth;i<HISTSIZE;i++)
190    {
191       if (graph_mths<16)
192       {
193          gdImageString(im,gdFontSmall,ci+((i-s_mth)*cs)+(((cw+co+co)-18)/2)+1,
194                      236,(unsigned char *)s_month[data[i].month-1],black);
195       }
196       else if (graph_mths<36)
197       {
198          gdImageChar(im,gdFontSmall,ci+((i-s_mth)*cs)+(((cw+co+co)-6)/2)+1,
199                      236,s_month[data[i].month-1][0],
200                      (data[i].month==1)?blue:black);
201       }
202       else
203       {
204          if (s_year!=data[i].year)  /* year change only */
205          {
206             if (data[i].month==1 && (i-s_mth)!=0)
207                gdImageChar(im,gdFontSmall, ci+((i-s_mth)*cs)-3,236,'|',blue);
208             j=(12-data[i].month+1)*cs;
209             if ((HISTSIZE-i)*cs < j) j=(HISTSIZE-i)*cs;
210             if (j>28)
211             {
212                /* format the year string */
213                sprintf(maxvaltxt, "%04d", data[i].year);
214                gdImageString(im,gdFontSmall,ci+((i-s_mth)*cs)+(j/2)-12,
215                              236, (unsigned char *)maxvaltxt, black);
216             }
217             s_year=data[i].year;
218          }
219       }
220 
221       if (data[i].hit   > maxval) maxval = data[i].hit;
222       if (data[i].files > maxval) maxval = data[i].files;
223       if (data[i].page  > maxval) maxval = data[i].page;
224    }
225    if (maxval <= 0) maxval = 1;
226    sprintf(maxvaltxt, "%llu", maxval);
227    gdImageStringUp(im,gdFontSmall,6,26+(strlen(maxvaltxt)*6),
228                    (unsigned char *)maxvaltxt,black);
229 
230    /* hits */
231    for (i=s_mth; i<HISTSIZE; i++)
232    {
233       percent = ((float)data[i].hit / (float)maxval);
234       if (percent <= 0.0) continue;
235       x1 = ci + ((i-s_mth)*cs);
236       x2 = x1 + cw;
237       y1 = 232 - (percent * 203);
238       gdImageFilledRectangle(im, x1, y1, x2, 232, HITCOLOR);
239       if (cw>2) gdImageRectangle(im, x1, y1, x2, 232, black);
240    }
241 
242    /* files */
243    for (i=s_mth; i<HISTSIZE; i++)
244    {
245       percent = ((float)data[i].files / (float)maxval);
246       if (percent <= 0.0) continue;
247       x1 = ci + co + ((i-s_mth)*cs);
248       x2 = x1 + cw;
249       y1 = 232 - (percent * 203);
250       gdImageFilledRectangle(im, x1, y1, x2, 232, FILECOLOR);
251       if (cw>2) gdImageRectangle(im, x1, y1, x2, 232, black);
252    }
253 
254    /* pages */
255    for (i=s_mth; i<HISTSIZE; i++)
256    {
257       percent = ((float)data[i].page / (float)maxval);
258       if (percent <= 0.0) continue;
259       x1 = ci + co + co + ((i-s_mth)*cs);
260       x2 = x1 + cw;
261       y1 = 232 - (percent * 203);
262       gdImageFilledRectangle(im, x1, y1, x2, 232, PAGECOLOR);
263       if (cw>2) gdImageRectangle(im, x1, y1, x2, 232, black);
264    }
265 
266    maxval=0;
267    for (i=s_mth; i<HISTSIZE; i++)
268    {
269        if (data[i].site  > maxval) maxval = data[i].site;
270        if (data[i].visit > maxval) maxval = data[i].visit;
271    }
272    if (maxval <= 0) maxval = 1;
273    sprintf(maxvaltxt, "%llu", maxval);
274    gdImageStringUp(im, gdFontSmall,493,26+(strlen(maxvaltxt)*6),
275                    (unsigned char *)maxvaltxt, black);
276 
277    cs = 180.0/graph_mths; cw = cs/2;
278    co = (48/graph_mths<1)?1:48/graph_mths;
279    ci = 308+((cw-co)/2);
280 
281    /* visits */
282    for (i=s_mth; i<HISTSIZE; i++)
283    {
284       percent = ((float)data[i].visit / (float)maxval);
285       if (percent <= 0.0) continue;
286       x1 = ci + ((i-s_mth)*cs);
287       x2 = x1 + cw;
288       y1 = 127 - (percent * 98);
289       gdImageFilledRectangle(im, x1, y1, x2, 127, VISITCOLOR);
290       if (cw>2) gdImageRectangle(im, x1, y1, x2, 127, black);
291    }
292 
293    /* sites */
294    for (i=s_mth; i<HISTSIZE; i++)
295    {
296       percent = ((float)data[i].site / (float)maxval);
297       if (percent <= 0.0) continue;
298       x1 = ci + co + ((i-s_mth)*cs);
299       x2 = x1 + cw;
300       y1 = 127 - (percent * 98);
301       gdImageFilledRectangle(im, x1, y1, x2, 127, SITECOLOR);
302       if (cw>2) gdImageRectangle(im, x1, y1, x2, 127, black);
303    }
304 
305    fmaxval=0.0;
306    for (i=s_mth; i<HISTSIZE; i++)
307        if (data[i].xfer > fmaxval) fmaxval = data[i].xfer;
308    if (fmaxval <= 0.0) fmaxval = 1.0;
309    sprintf(maxvaltxt, "%.0f", fmaxval);
310    gdImageStringUp(im, gdFontSmall,493,130+(strlen(maxvaltxt)*6),
311                    (unsigned char *)maxvaltxt,black);
312 
313    cs = 180.0/graph_mths; cw = (cs/2)+(co/2);
314    ci = 308+((cw-co)/2);
315 
316    /* xfer */
317    for (i=s_mth; i<HISTSIZE; i++)
318    {
319       percent = ((float)data[i].xfer / (float)fmaxval);
320       if (percent <= 0.0) continue;
321       x1 = ci+ ((i-s_mth)*cs);
322       x2 = x1 + cw;
323       y1 = 232 - (percent * 98);
324       gdImageFilledRectangle(im, x1, y1, x2, 232, KBYTECOLOR);
325       if (cw>2) gdImageRectangle(im, x1, y1, x2, 232, black);
326    }
327 
328    /* stat the file */
329    if ( !(lstat(fname, &out_stat)) )
330    {
331       /* check if the file a symlink */
332       if ( S_ISLNK(out_stat.st_mode) )
333       {
334          if (verbose)
335          fprintf(stderr,"%s %s (symlink)\n",msg_no_open,fname);
336          return(EBADF);
337       }
338    }
339 
340    /* save PNG image */
341    if ((out = fopen(fname, "wb")) != NULL)
342    {
343       gdImagePng(im, out);
344       fclose(out);
345    }
346    /* deallocate memory */
347    gdImageDestroy(im);
348 
349    return (0);
350 }
351 
352 /*****************************************************************/
353 /*                                                               */
354 /* MONTH_GRAPH6  - Month graph with six data sets                */
355 /*                                                               */
356 /*****************************************************************/
357 
358 #define YSIZE 400
359 
month_graph6(char * fname,char * title,int month,int year,u_int64_t data1[31],u_int64_t data2[31],u_int64_t data3[31],double data4[31],u_int64_t data5[31],u_int64_t data6[31])360 int month_graph6(     char  *fname,        /* filename           */
361                       char  *title,        /* graph title        */
362                       int   month,         /* graph month        */
363                       int   year,          /* graph year         */
364                  u_int64_t  data1[31],     /* data1 (hits)       */
365                  u_int64_t  data2[31],     /* data2 (files)      */
366                  u_int64_t  data3[31],     /* data3 (sites)      */
367                  double     data4[31],     /* data4 (kbytes)     */
368                  u_int64_t  data5[31],     /* data5 (views)      */
369                  u_int64_t  data6[31])     /* data6 (visits)     */
370 {
371 
372    /* local variables */
373    int         i,j,s,x1,y1,x2;
374    u_int64_t   maxval=0;
375    double      fmaxval=0.0;
376 
377    /* calc julian date for month */
378    julday = (jdate(1, month,year) % 7);
379 
380    /* initalize the graph */
381    init_graph(title,512,400);
382 
383    gdImageLine(im, 21, 180, 490, 180, black); /* draw section lines */
384    gdImageLine(im, 21, 179, 490, 179, white);
385    gdImageLine(im, 21, 280, 490, 280, black);
386    gdImageLine(im, 21, 279, 490, 279, white);
387 
388    /* index lines? */
389    if (graph_lines)
390    {
391       y1=154/(graph_lines+1);
392       for (i=0;i<graph_lines;i++)
393        gdImageLine(im,21,((i+1)*y1)+25,489,((i+1)*y1)+25,dkgrey);
394       y1=100/(graph_lines+1);
395       for (i=0;i<graph_lines;i++)
396        gdImageLine(im,21,((i+1)*y1)+180,489,((i+1)*y1)+180,dkgrey);
397       for (i=0;i<graph_lines;i++)
398        gdImageLine(im,21,((i+1)*y1)+280,489,((i+1)*y1)+280,dkgrey);
399    }
400 
401    /* x-axis legend */
402    for (i=0;i<31;i++)
403    {
404       if ((julday % 7 == 6) || (julday % 7 == 0))
405        gdImageString(im,gdFontSmall,25+(i*15),382,
406                      (unsigned char *)numchar[i+1],HITCOLOR);
407       else
408        gdImageString(im,gdFontSmall,25+(i*15),382,
409                      (unsigned char *)numchar[i+1],black);
410       julday++;
411    }
412 
413    /* y-axis legend */
414    for (i=0; i<31; i++)
415    {
416        if (data1[i] > maxval) maxval = data1[i];           /* get max val    */
417        if (data2[i] > maxval) maxval = data2[i];
418        if (data5[i] > maxval) maxval = data5[i];
419    }
420    if (maxval <= 0) maxval = 1;
421    sprintf(maxvaltxt, "%llu", maxval);
422    gdImageStringUp(im, gdFontSmall,8,26+(strlen(maxvaltxt)*6),
423                    (unsigned char *)maxvaltxt,black);
424 
425    if (graph_legend)                           /* Print color coded legends? */
426    {
427       /* Kbytes Legend */
428       gdImageStringUp(im,gdFontSmall,494,376,
429                       (unsigned char *)msg_h_xfer,dkgrey);
430       gdImageStringUp(im,gdFontSmall,493,375,
431                       (unsigned char *)msg_h_xfer,KBYTECOLOR);
432 
433       /* Sites/Visits Legend */
434       i = (strlen(msg_h_sites)*6);
435       gdImageStringUp(im,gdFontSmall,494,276,
436                       (unsigned char *)msg_h_sites,dkgrey);
437       gdImageStringUp(im,gdFontSmall,493,275,
438                       (unsigned char *)msg_h_sites,SITECOLOR);
439       gdImageStringUp(im,gdFontSmall,494,276-i-3,(unsigned char *)"/",dkgrey);
440       gdImageStringUp(im,gdFontSmall,493,275-i-3,(unsigned char *)"/",black);
441       gdImageStringUp(im,gdFontSmall,494,276-i-12,
442                       (unsigned char *)msg_h_visits,dkgrey);
443       gdImageStringUp(im,gdFontSmall,493,275-i-12,
444                       (unsigned char *)msg_h_visits,VISITCOLOR);
445 
446       /* Pages/Files/Hits Legend */
447       s = ( i=(strlen(msg_h_pages)*6) )+
448           ( j=(strlen(msg_h_files)*6) )+
449           ( strlen(msg_h_hits)*6 )+ 52;
450       gdImageStringUp(im,gdFontSmall,494,s,
451                       (unsigned char *)msg_h_pages,dkgrey);
452       gdImageStringUp(im,gdFontSmall,493,s-1,
453                       (unsigned char *)msg_h_pages,PAGECOLOR);
454       gdImageStringUp(im,gdFontSmall,494,s-i-3,(unsigned char *)"/",dkgrey);
455       gdImageStringUp(im,gdFontSmall,493,s-i-4,(unsigned char *)"/",black);
456       gdImageStringUp(im,gdFontSmall,494,s-i-12,
457                       (unsigned char *)msg_h_files,dkgrey);
458       gdImageStringUp(im,gdFontSmall,493,s-i-13,
459                       (unsigned char *)msg_h_files,FILECOLOR);
460       gdImageStringUp(im,gdFontSmall,494,s-i-j-15,(unsigned char *)"/",dkgrey);
461       gdImageStringUp(im,gdFontSmall,493,s-i-j-16,(unsigned char *)"/",black);
462       gdImageStringUp(im,gdFontSmall,494,s-i-j-24,
463                       (unsigned char *)msg_h_hits,dkgrey);
464       gdImageStringUp(im,gdFontSmall,493,s-i-j-25,
465                       (unsigned char *)msg_h_hits,HITCOLOR);
466    }
467 
468    /* data1 */
469    for (i=0; i<31; i++)
470    {
471       percent = ((float)data1[i] / (float)maxval);
472       if (percent <= 0.0) continue;
473       x1 = 25 + (i*15);
474       x2 = x1 + 7;
475       y1 = 176 - (percent * 147);
476       gdImageFilledRectangle(im, x1, y1, x2, 176, HITCOLOR);
477       gdImageRectangle(im, x1, y1, x2, 176, black);
478    }
479 
480    /* data2 */
481    for (i=0; i<31; i++)
482    {
483       percent = ((float)data2[i] / (float)maxval);
484       if (percent <= 0.0) continue;
485       x1 = 27 + (i*15);
486       x2 = x1 + 7;
487       y1 = 176 - (percent * 147);
488       gdImageFilledRectangle(im, x1, y1, x2, 176, FILECOLOR);
489       gdImageRectangle(im, x1, y1, x2, 176, black);
490    }
491 
492    /* data5 */
493    for (i=0; i<31; i++)
494    {
495       if (data5[i]==0) continue;
496       percent = ((float)data5[i] / (float)maxval);
497       if (percent <= 0.0) continue;
498       x1 = 29 + (i*15);
499       x2 = x1 + 7;
500       y1 = 176 - (percent * 147);
501       gdImageFilledRectangle(im, x1, y1, x2, 176, PAGECOLOR);
502       gdImageRectangle(im, x1, y1, x2, 176, black);
503    }
504 
505    /* sites / visits */
506    maxval=0;
507    for (i=0; i<31; i++)
508    {
509       if (data3[i]>maxval) maxval = data3[i];
510       if (data6[i]>maxval) maxval = data6[i];
511    }
512    if (maxval <= 0) maxval = 1;
513    sprintf(maxvaltxt, "%llu", maxval);
514    gdImageStringUp(im, gdFontSmall,8,180+(strlen(maxvaltxt)*6),
515                    (unsigned char *)maxvaltxt, black);
516 
517    /* data 6 */
518    for (i=0; i<31; i++)
519    {
520       percent = ((float)data6[i] / (float)maxval);
521       if (percent <= 0.0) continue;
522       x1 = 25 + (i*15);
523       x2 = x1 + 8;
524       y1 = 276 - (percent * 92);
525       gdImageFilledRectangle(im, x1, y1, x2, 276, VISITCOLOR);
526       gdImageRectangle(im, x1, y1, x2, 276, black);
527    }
528 
529    /* data 3 */
530    for (i=0; i<31; i++)
531    {
532       percent = ((float)data3[i] / (float)maxval);
533       if (percent <= 0.0) continue;
534       x1 = 29 + (i*15);
535       x2 = x1 + 7;
536       y1 = 276 - (percent * 92);
537       gdImageFilledRectangle(im, x1, y1, x2, 276, SITECOLOR);
538       gdImageRectangle(im, x1, y1, x2, 276, black);
539    }
540 
541    /* data4 */
542    fmaxval=0.0;
543    for (i=0; i<31; i++)
544       if (data4[i]>fmaxval) fmaxval = data4[i];
545    if (fmaxval <= 0.0) fmaxval = 1.0;
546    sprintf(maxvaltxt, "%.0f", fmaxval/1024);
547    gdImageStringUp(im, gdFontSmall,8,280+(strlen(maxvaltxt)*6),
548                    (unsigned char *)maxvaltxt, black);
549 
550    for (i=0; i<31; i++)
551    {
552       percent = data4[i] / fmaxval;
553       if (percent <= 0.0) continue;
554       x1 = 26 + (i*15);
555       x2 = x1 + 10;
556       y1 = 375 - ( percent * 91 );
557       gdImageFilledRectangle(im, x1, y1, x2, 375, KBYTECOLOR);
558       gdImageRectangle(im, x1, y1, x2, 375, black);
559    }
560 
561    /* stat the file */
562    if ( !(lstat(fname, &out_stat)) )
563    {
564       /* check if the file a symlink */
565       if ( S_ISLNK(out_stat.st_mode) )
566       {
567          if (verbose)
568          fprintf(stderr,"%s %s (symlink)\n",msg_no_open,fname);
569          return(EBADF);
570       }
571    }
572 
573    /* save PNG image */
574    if ((out = fopen(fname, "wb")) != NULL)
575    {
576       gdImagePng(im, out);
577       fclose(out);
578    }
579    /* deallocate memory */
580    gdImageDestroy(im);
581 
582    return (0);
583 }
584 
585 /*****************************************************************/
586 /*                                                               */
587 /* DAY_GRAPH3  - Day graph with three data sets                  */
588 /*                                                               */
589 /*****************************************************************/
590 
day_graph3(char * fname,char * title,u_int64_t data1[24],u_int64_t data2[24],u_int64_t data3[24])591 int day_graph3(     char  *fname,
592                     char  *title,
593                u_int64_t  data1[24],
594                u_int64_t  data2[24],
595                u_int64_t  data3[24])
596 {
597 
598    /* local variables */
599    int       i,j,s,x1,y1,x2;
600    u_int64_t maxval=0;
601 
602    /* initalize the graph */
603    init_graph(title,512,256);
604 
605    /* index lines? */
606    if (graph_lines)
607    {
608       y1=210/(graph_lines+1);
609       for (i=0;i<graph_lines;i++)
610        gdImageLine(im,21,((i+1)*y1)+25,489,((i+1)*y1)+25,dkgrey);
611    }
612 
613    /* x-axis legend */
614    for (i=0;i<24;i++)
615    {
616       gdImageString(im,gdFontSmall,33+(i*19),238,
617                     (unsigned char *)numchar[i],black);
618       if (data1[i] > maxval) maxval = data1[i];           /* get max val    */
619       if (data2[i] > maxval) maxval = data2[i];
620       if (data3[i] > maxval) maxval = data3[i];
621    }
622    if (maxval <= 0) maxval = 1;
623    sprintf(maxvaltxt, "%llu", maxval);
624    gdImageStringUp(im, gdFontSmall, 8, 26+(strlen(maxvaltxt)*6),
625                    (unsigned char *)maxvaltxt, black);
626 
627    if (graph_legend)                          /* print color coded legends? */
628    {
629       /* Pages/Files/Hits Legend */
630       s = ( i=(strlen(msg_h_pages)*6) )+
631           ( j=(strlen(msg_h_files)*6) )+
632           ( strlen(msg_h_hits)*6 )+ 52;
633       gdImageStringUp(im,gdFontSmall,494,s,
634                       (unsigned char *)msg_h_pages,dkgrey);
635       gdImageStringUp(im,gdFontSmall,493,s-1,
636                       (unsigned char *)msg_h_pages,PAGECOLOR);
637       gdImageStringUp(im,gdFontSmall,494,s-i-3,(unsigned char *)"/",dkgrey);
638       gdImageStringUp(im,gdFontSmall,493,s-i-4,(unsigned char *)"/",black);
639       gdImageStringUp(im,gdFontSmall,494,s-i-12,
640                       (unsigned char *)msg_h_files,dkgrey);
641       gdImageStringUp(im,gdFontSmall,493,s-i-13,
642                       (unsigned char *)msg_h_files,FILECOLOR);
643       gdImageStringUp(im,gdFontSmall,494,s-i-j-15,(unsigned char *)"/",dkgrey);
644       gdImageStringUp(im,gdFontSmall,493,s-i-j-16,(unsigned char *)"/",black);
645       gdImageStringUp(im,gdFontSmall,494,s-i-j-24,
646                       (unsigned char *)msg_h_hits,dkgrey);
647       gdImageStringUp(im,gdFontSmall,493,s-i-j-25,
648                       (unsigned char *)msg_h_hits,HITCOLOR);
649    }
650 
651    /* data1 */
652    for (i=0; i<24; i++)
653    {
654       percent = ((float)data1[i] / (float)maxval);  /* percent of 100% */
655       if (percent <= 0.0) continue;
656       x1 = 29 + (i*19);
657       x2 = x1 + 10;
658       y1 = 232 - (percent * 203);
659       gdImageFilledRectangle(im, x1, y1, x2, 232, HITCOLOR);
660       gdImageRectangle(im, x1, y1, x2, 232, black);
661    }
662 
663    /* data2 */
664    for (i=0; i<24; i++)
665    {
666       percent = ((float)data2[i] / (float)maxval);  /* percent of 100% */
667       if (percent <= 0.0) continue;
668       x1 = 32 + (i*19);
669       x2 = x1 + 10;
670       y1 = 232 - (percent * 203);
671       gdImageFilledRectangle(im, x1, y1, x2, 232, FILECOLOR);
672       gdImageRectangle(im, x1, y1, x2, 232, black);
673    }
674 
675    /* data3 */
676    for (i=0; i<24; i++)
677    {
678       percent = ((float)data3[i] / (float)maxval);  /* percent of 100% */
679       if (percent <= 0.0) continue;
680       x1 = 35 + (i*19);
681       x2 = x1 + 10;
682       y1 = 232 - (percent * 203);
683       gdImageFilledRectangle(im, x1, y1, x2, 232, PAGECOLOR);
684       gdImageRectangle(im, x1, y1, x2, 232, black);
685    }
686 
687    /* stat the file */
688    if ( !(lstat(fname, &out_stat)) )
689    {
690       /* check if the file a symlink */
691       if ( S_ISLNK(out_stat.st_mode) )
692       {
693          if (verbose)
694          fprintf(stderr,"%s %s (symlink)\n",msg_no_open,fname);
695          return(EBADF);
696       }
697    }
698 
699    /* save PNG image */
700    if ((out = fopen(fname, "wb")) != NULL)
701    {
702       gdImagePng(im, out);
703       fclose(out);
704    }
705    /* deallocate memory */
706    gdImageDestroy(im);
707 
708    return (0);
709 }
710 
711 /*****************************************************************/
712 /*                                                               */
713 /* PIE_CHART  - draw a pie chart (10 data items max)             */
714 /*                                                               */
715 /*****************************************************************/
716 
pie_chart(char * fname,char * title,u_int64_t t_val,u_int64_t data1[],char * legend[])717 int pie_chart(char *fname, char *title, u_int64_t t_val,
718               u_int64_t data1[], char *legend[])
719 {
720    int i,x,percent,y=47;
721    double s_arc=0.0;
722    int purple_or_pie1, ltgreen_or_pie2, ltpurple_or_pie3, brown_or_pie4;
723    int r, g, b;
724    char buffer[128];
725 
726    struct pie_data gdata;
727 
728    /* init graph and colors */
729    init_graph(title,512,300);
730    r=getred(pie_color1); g=getgreen(pie_color1); b=getblue(pie_color1);
731    purple_or_pie1  = gdImageColorAllocate(im, r, g, b);
732    r=getred(pie_color2); g=getgreen(pie_color2); b=getblue(pie_color2);
733    ltgreen_or_pie2 = gdImageColorAllocate(im, r, g, b);
734    r=getred(pie_color3); g=getgreen(pie_color3); b=getblue(pie_color3);
735    ltpurple_or_pie3= gdImageColorAllocate(im, r, g, b);
736    r=getred(pie_color4); g=getgreen(pie_color4); b=getblue(pie_color4);
737    brown_or_pie4 = gdImageColorAllocate(im, r, g, b);
738 
739    /* do the circle... */
740    gdImageArc(im, CX, CY, XRAD, YRAD, 0, 360, black);
741    gdImageArc(im, CX, CY+10, XRAD-2, YRAD-2, 2, 178, black);
742    gdImageFillToBorder(im, CX, CY+(YRAD/2)+1, black, black);
743 
744    /* slice the pie */
745    gdata=*calc_arc(0.0,0.0);
746    gdImageLine(im,CX,CY,gdata.x,gdata.y,black);  /* inital line           */
747 
748    for (i=0;i<10;i++)                      /* run through data array      */
749    {
750       if ((data1[i]!=0)&&(s_arc<1.0))      /* make sure valid slice       */
751       {
752          percent=(((double)data1[i]/t_val)+0.005)*100.0;
753          if (percent<1) break;
754 
755          if (s_arc+((double)percent/100.0)>=1.0)
756          {
757             gdata=*calc_arc(s_arc,1.0);
758             s_arc=1.0;
759          }
760          else
761          {
762             gdata=*calc_arc(s_arc,s_arc+((double)percent/100.0));
763             s_arc+=(double)percent/100.0;
764          }
765 
766          gdImageLine(im, CX, CY, gdata.x, gdata.y, black);
767          gdImageFill(im, gdata.mx, gdata.my, i+5);
768 
769 	 snprintf(buffer,sizeof(buffer),"%s (%d%%)",legend[i], percent);
770          x=480-(strlen(buffer)*7);
771          gdImageString(im,gdFontMediumBold, x+1, y+1,
772                        (unsigned char *)buffer, black);
773          gdImageString(im,gdFontMediumBold, x, y,
774                        (unsigned char *)buffer, i+5);
775          y+=20;
776       }
777    }
778 
779    if (s_arc < 1.0)                         /* anything left over?        */
780    {
781       gdata=*calc_arc(s_arc,1.0);
782 
783       gdImageFill(im, gdata.mx, gdata.my, white);
784       snprintf(buffer,sizeof(buffer),"%s (%d%%)",
785            msg_h_other,100-(int)(s_arc*100));
786       x=480-(strlen(buffer)*7);
787       gdImageString(im,gdFontMediumBold, x+1, y+1,
788                     (unsigned char *)buffer, black);
789       gdImageString(im,gdFontMediumBold, x, y,
790                     (unsigned char *)buffer, white);
791    }
792 
793    /* stat the file */
794    if ( !(lstat(fname, &out_stat)) )
795    {
796       /* check if the file a symlink */
797       if ( S_ISLNK(out_stat.st_mode) )
798       {
799          if (verbose)
800          fprintf(stderr,"%s %s (symlink)\n",msg_no_open,fname);
801          return(EBADF);
802       }
803    }
804 
805    /* save PNG image */
806    if ((out = fopen(fname, "wb")) != NULL)
807    {
808       gdImagePng(im, out);
809       fclose(out);
810    }
811    /* deallocate memory */
812    gdImageDestroy(im);
813 
814    return (0);
815 }
816 
817 /*****************************************************************/
818 /*                                                               */
819 /* CALC_ARC  - generate x,y coordinates for pie chart            */
820 /*                                                               */
821 /*****************************************************************/
822 
calc_arc(float min,float max)823 struct pie_data *calc_arc(float min, float max)
824 {
825    static struct pie_data data;
826    double d;
827 
828    /* Calculate max line */
829    d=max;
830    data.x=cos(d*(2*PI))*((XRAD-2)/2)+CX;
831    data.y=sin(d*(2*PI))*((YRAD-2)/2)+CY;
832    /* Now get mid-point  */
833    d=((min+max)/2);
834    data.mx=cos(d*(2*PI))*(XRAD/3)+CX;
835    data.my=sin(d*(2*PI))*(YRAD/3)+CY;
836    return &data;
837 }
838 
839 /*****************************************************************/
840 /*                                                               */
841 /* INIT_GRAPH  - initalize graph and draw borders                */
842 /*                                                               */
843 /*****************************************************************/
844 
init_graph(char * title,int xsize,int ysize)845 void init_graph(char *title, int xsize, int ysize)
846 {
847    int i, r, g, b;
848 
849    im = gdImageCreate(xsize,ysize);
850 
851    /* allocate color maps, background color first (grey) */
852    grey    = gdImageColorAllocate(im, 192, 192, 192);
853    dkgrey  = gdImageColorAllocate(im, 128, 128, 128);
854    black   = gdImageColorAllocate(im, 0, 0, 0);
855    white   = gdImageColorAllocate(im, 255, 255, 255);
856    blue    = gdImageColorAllocate(im, 0, 0, 255);
857    r=getred(hit_color); g=getgreen(hit_color); b=getblue(hit_color);
858    hit_or_green = gdImageColorAllocate(im, r, g, b);
859    r=getred(site_color); g=getgreen(site_color); b=getblue(site_color);
860    site_or_orange = gdImageColorAllocate(im, r, g, b);
861    r=getred(file_color); g=getgreen(file_color); b=getblue(file_color);
862    file_or_blue = gdImageColorAllocate(im, r, g, b);
863    r=getred(kbyte_color); g=getgreen(kbyte_color); b=getblue(kbyte_color);
864    kbyte_or_red = gdImageColorAllocate(im, r, g, b);
865    r=getred(page_color); g=getgreen(page_color); b=getblue(page_color);
866    page_or_cyan = gdImageColorAllocate(im, r, g, b);
867    r=getred(visit_color); g=getgreen(visit_color); b=getblue(visit_color);
868    visit_or_yellow = gdImageColorAllocate(im, r, g, b);
869 
870    /* black outside border */
871    gdImageRectangle(im, 0, 0, xsize-1, ysize-1, black);
872 
873    /* do shadow effect (bevel) border */
874    for (i=1; i<5 ;i++)
875    {
876       gdImageLine(im, i, i, xsize-i-2, i, white);
877       gdImageLine(im, i, i, i, ysize-i-2, white);
878       gdImageLine(im, i+1, ysize-i-1, xsize-i-1, ysize-i-1, dkgrey);
879       gdImageLine(im, xsize-i-1, i+1, xsize-i-1, ysize-i-1, dkgrey);
880    }
881 
882    /* generic inside shadow box */
883    gdImageRectangle(im, 20, 25, xsize-21, ysize-21, black);
884    gdImageRectangle(im, 19, 24, xsize-22, ysize-22, white);
885 
886    /* display the graph title */
887    gdImageString(im, gdFontMediumBold, 20, 8,
888                  (unsigned char *)title, blue);
889 
890    return;
891 }
892 
893 /****************************************************************/
894 /*                                                              */
895 /* ASHEX2INT - ASCII HEX TO INT CONVERTER                       */
896 /*                                                              */
897 /****************************************************************/
898 
ashex2int(char * str)899 int ashex2int(char *str)
900 {
901    /* returns base-10 integer value from a 2 ASCII hex number   */
902    return from_hex(str[1])+(from_hex(str[0])*16);
903 }
904