1<?php
2
3/* ************************************************************************
4 amavis-stats.php - build rrd graphs from amavis-stats collected data.
5
6 Copyright (C) 2003,2004 Mark Lawrence (nomad@null.net)
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
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 with
19 the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
20 if not, write to the Free Software Foundation, Inc., 59 Temple Place,
21 Suite 330, Boston, MA  02111-1307  USA
22
23 On Debian systems, the complete text of the GNU General Public
24 License, version 2, can be found in /usr/share/common-licenses/GPL-2.
25
26************************************************************************ */
27
28/*
29*
30* This script depends on rrdtool|php4-rrdtool There are three ways to use it:
31*
32* 1. Directly from the command line as "php4 amavis-stats.php".
33*    This allows you to generate graphs without having to have a full-blown
34*    apache or other webserver environment running on your virus/smtp host.
35*
36*    If you would like regular graphs generated you could put something
37*    like this in /etc/cron.d/virus-graphs:
38*
39*    #
40*    # cron job to generate and move graphs every hour
41*    #
42*    0 * * * * amavis cd /var/tmp && php4 /usr/share/.../amavis-stats.php \
43*    && rsync -avz /var/tmp/*.png remotehost:
44*
45* 2. Copy the script to somewhere in your web root (eg /var/www) and
46*    call it directly as a url (eg http://server/amavis-stats.php).
47*
48* 2. Include it from another php script. In this case no html will be
49*    generated directly by amavis-stats.php. However you then have the
50*    ability to call the as[V|P]Graph() functions (after a call to
51*    asLoadStats()) directly from within your script with your desired
52*    parameters. Then build your own html to display the graphs.
53*
54*    Hopefully the function names are unique enough to not clash
55*    with your own namespace.
56*
57*/
58
59global $asVersion;
60$asVersion = "0.1.12";
61
62// The rrdstep value MUST match that which is used by the perl script.
63// don't change one without the other.
64$rrdstep = 300;
65
66// ////////////////////////////////////////////////////////////////////////
67//
68// ////////////////////////////////////////////////////////////////////////
69
70/*
71* php version of the RRDTool::rrd_graph extention using shell executable.
72* This needs to come first in the file otherwise the php pre-compiler
73* thinks we don't exist...
74*/
75
76$rrd = "shared-library";
77
78if (!function_exists('rrd_graph') && !@dl('rrdtool.so')) {
79    $rrd = "command-line";
80
81    function rrd_graph ($img, $opts, $count) {
82        global $rrd_error_val;
83
84        unset($output);
85
86        $cmd = "rrdtool graph $img '". implode("' \\\n'",$opts) . "'" .
87               " 2>&1";
88        asDbg($cmd, 1);
89        $out = exec($cmd, $output, $rrd_error_val);
90
91        if ($rrd_error_val) {
92            if (!is_numeric($rrd_error_val)) {
93                $rrd_error_val = $output[0];
94            }
95            return false;
96        }
97
98        asDbg($output);
99
100        if (preg_match("/(\d+)x(\d+)/", $output[0], $matches)) {
101            $retval["xsize"] = $matches[1];
102            $retval["ysize"] = $matches[2];
103        } else {
104            $rrd_error_val = $output[0];
105            return false;
106        }
107
108        array_shift($output);
109
110        foreach ($output as $index => $value) {
111            $retval[calcpr][$index] = $value;
112        }
113        return $retval;
114    }
115
116    function rrd_error() {
117        global $rrd_error_val;
118        return $rrd_error_val;
119    }
120}
121
122
123/*
124* Timing function to work out how long things take
125*/
126function elapsed($start)
127{
128    $end = microtime();
129    list($start2, $start1) = explode(" ", $start);
130    list($end2, $end1) = explode(" ", $end);
131    $diff1 = $end1 - $start1;
132    $diff2 = $end2 - $start2;
133    if( $diff2 < 0 ){
134        $diff1 -= 1;
135        $diff2 += 1.0;
136    }
137    return $diff2 + $diff1;
138}
139
140
141
142if (isset($_SERVER["REQUEST_URI"])) {   // web environment
143    $fullpage = true;
144    $outdir = "img";
145
146} else { // Stand-alone command line usage
147    $cmd = true;
148    $outdir = $_SERVER["PWD"];
149
150}
151
152if (isset($GLOBALS["rate"])) {
153    $rate = $GLOBALS["rate"];
154} else {
155    $rate = 60;
156}
157
158
159if (isset($fullpage) || isset($cmd)) {
160    asHtmlStart();
161
162    if (asLoadStats()) {
163
164        $minsec  = 60;
165        $hoursec = 60 * $minsec;
166        $daysec  = 24 * $hoursec;
167        $weeksec =  7 * $daysec;
168
169        $now = time();
170
171        print "<h2>Daily Graphs</h2>\n";
172        print asPGraph("$outdir/passed-day.png", $now, $daysec, "by day");
173        print asVGraph("$outdir/virus-day.png", $now, $daysec, "by day");
174
175        print "<h2>Weekly Graphs</h2>\n";
176        print asPGraph("$outdir/passed-week.png", $now, 7*$daysec, "by week");
177        print asVGraph("$outdir/virus-week.png", $now, 7*$daysec, "by week");
178
179        print "<h2>Monthly Graphs</h2>\n";
180        print asPGraph("$outdir/passed-month.png", $now, 31*$daysec, "by month");
181        print asVGraph("$outdir/virus-month.png", $now, 31*$daysec, "by month");
182
183        print "<h2>Yearly Graphs</h2>\n";
184        print asPGraph("$outdir/passed-year.png", $now, 365*$daysec, "by year");
185        print asVGraph("$outdir/virus-year.png", $now, 365*$daysec, "by year");
186
187    } else {
188        asMsg("No statistics available.");
189    }
190
191    asHtmlEnd();
192}
193
194function asDbg($txt = "", $pre = 0) {
195    if ($GLOBALS["debug"]) {
196        if ($pre) {
197            print "<pre>\n";
198        }
199        print "amavis-stats::debug: $txt\n";
200        if (is_array($txt)) {
201            print "<pre>\n";
202            print_r($txt);
203            print "</pre>\n";
204        }
205        if ($pre) {
206            print "</pre>\n";
207        }
208        print "<br>\n";
209
210    }
211}
212
213function asMsg($txt = "") {
214    print "amavis-stats: $txt<br>\n";
215}
216
217function asErr($txt = "") {
218    print "amavis-stats::error: $txt<br>\n";
219}
220
221
222function asHtmlStart() {
223    print '
224<!DOCTYPE HTML PUBLIC \-//W3C//DTD HTML 3.2 Final//EN\>
225<html>
226<head>
227<style>
228    body {
229        font-size: x-small;
230        font-family: sans-serif;
231        margin: 1em;
232    }
233    img {
234        margin-bottom: 1em;
235        text-align: center;
236        clear: both;
237    }
238</style>
239</head>
240<body>
241
242<h1>Amavis Detection Statistics</h1>';
243
244
245}
246
247
248function asHtmlEnd() {
249    global $asVersion, $rrd;
250    print '<br>
251<span style=\"font-size:x-small\">[Generated by <a
252href="http://rekudos.net/amavis-stats/">amavis-stats</a>
253 version ' . $asVersion . ' using ' . $rrd . ' rrdtool [';
254
255    if (!$GLOBALS["debug"]) {
256        print '<a href="' . $GLOBALS["REQUEST_URI"] .
257              '?debug=1">debug</a>';
258    } else {
259        print '<a href="' .
260              str_replace("?debug=1", "", $GLOBALS["REQUEST_URI"]) .
261              '">nodebug</a>';
262    }
263    print "].\n";
264
265    if (!$GLOBALS["rate"] || ($GLOBALS["rate"] == 60)) {
266        print 'Rate per <a href="' . $GLOBALS["REQUEST_URI"] .
267              '?rate=3600">hour</a>';
268    } else {
269        print 'Rate per <a href="' .
270              str_replace("?rate=3600", "", $GLOBALS["REQUEST_URI"]) .
271              '">minute</a>';
272    }
273
274
275    print ']</span>
276</body>
277</html>
278    ';
279}
280
281
282
283/*
284*
285*/
286function asLoadStats () {
287    global $as_libdir, $virus, $pid, $psid, $iid, $bid,$nid,$nsid, $lastupdate, $maxi;
288
289    $as_libdir    = "/usr/local/www/amavis-stats";
290    $as_statefile = $as_libdir . "/amavis-stats.state";
291    $as_namefile  = $as_libdir . "/amavis-stats.names";
292    $as_seenfile  = $as_libdir . "/amavis-stats.seen";
293
294    $readfile = @file($as_namefile);
295    if (!is_array($readfile)) {
296        asErr("Couldn't open id => name mappings file.");
297    }
298    asDbg("$as_namefile");
299    asDbg($readfile);
300
301    $virus = array();
302    $pid = -1;
303    $iid = -1;
304    $bid = -1;
305    $sid = -1;
306
307    for ($k = 0; $k <= (count($readfile) - 1); $k++) {
308        $fields = preg_split("/\s+/",$readfile[$k], 2, PREG_SPLIT_NO_EMPTY);
309
310        $fields[0] = trim($fields[0]);
311        $fields[1] = trim($fields[1]);
312
313        $virus[$fields[0]]["id"] = $fields[0];
314        $virus[$fields[0]]["name"] = $fields[1];
315        if ($fields[1] == "Passed") {
316            $pid = $fields[0];
317        }
318        elseif ($fields[1] == "Passed(SPAM)") {
319            $psid = $fields[0];
320        }
321        elseif ($fields[1] == "Infected") {
322            $iid = $fields[0];
323        }
324        elseif ($fields[1] == "Banned") {
325            $bid = $fields[0];
326        }
327        elseif ($fields[1] == "Not-Delivered") {
328            $nid = $fields[0];
329        }
330        elseif ($fields[1] == "Not-Delivered(SPAM)") {
331            $nsid = $fields[0];
332        }
333    }
334
335    $readfile = @file($as_seenfile);
336    if (!is_array($readfile)) {
337        asErr("Couldn't open first/last seen file.");
338    }
339    asDbg("$as_seenfile");
340    asDbg($readfile);
341
342    for ($k = 0; $k <= (count($readfile) - 1); $k++) {
343        $fields = preg_split("/\s+/",$readfile[$k], -1, PREG_SPLIT_NO_EMPTY);
344        $virus[$fields[0]]["firstseen"] = $fields[1];
345        $virus[$fields[0]]["lastseen"] = $fields[2];
346    }
347
348    $readfile = @file($as_statefile);
349    if (!is_array($readfile)) {
350        asErr("Couldn't open state file.");
351    }
352    asDbg("$as_statefile");
353    asDbg($readfile);
354
355    for ($k = 0; $k <= (count($readfile) - 1); $k++) {
356        $fields = preg_split("/\s+/",$readfile[$k], -1, PREG_SPLIT_NO_EMPTY);
357        if ($fields[0] == "lastupdate:" && is_numeric($fields[1])) {
358            $lastupdate = $fields[1];
359        } elseif ($fields[0] == "LC_TIME:") {
360            setlocale(LC_TIME, $fields[1]);
361        }
362    }
363
364    if (!isset($lastupdate)) {
365        asErr("lastupdate not defined.");
366        return false;
367    }
368    elseif ($lastupdate == 0) {
369        asErr("last update was at 0 seconds.");
370        return false;
371    }
372
373    as_col();
374    return true;
375}
376
377
378
379function as_col() {
380    global $as_colors;
381    $as_colors = array(
382    "#00BFFF",  /* DeepSkyBlue */
383    "#FFD700",  /* gold */
384    "#FA8072",  /* salmon */
385    "#006400",  /* DarkGreen */
386    "#FF1493",  /* DeepPink */
387    "#00CED1",  /* DarkTurquoise */
388    "#FF00FF",  /* magenta */
389    "#00FF7F",  /* SpringGreen */
390    "#FF0000",  /* red */
391    "#228B22",  /* ForestGreen */
392    "#F0E68C",  /* khaki */
393    "#FFFF00",  /* yellow */
394    "#0000FF",  /* blue */
395    "#CD5C5C",  /* IndianRed */
396    "#6A5ACD",  /* SlateBlue */
397    "#F4A460",  /* SandyBrown */
398    "#FFA500",  /* orange */
399    "#FF8C00",  /* DarkOrange */
400    "#000080",  /* NavyBlue */
401    "#FF69B4",  /* HotPink */
402    "#2E8B57",  /* SeaGreen */
403    "#A020F0",  /* purple */
404    "#FFB6C1",  /* LightPink */
405    "#0000CD",  /* MediumBlue */
406    "#B22222",  /* firebrick */
407    "#7CFC00",  /* LawnGreen */
408    "#D02090",  /* VioletRed */
409    "#6495ED"  /* CornflowerBlue */
410    );
411}
412
413
414
415
416/*
417*
418*/
419function addopts(&$opts, $type, $id, $vcount, $virus, $length) {
420    global $as_libdir, $as_colors, $rate, $maxi;
421
422    $name = sprintf("%-".$maxi."s", $virus[$id]["name"]);
423    $count = $vcount[$id];
424    $count = sprintf("%8d", $count);
425//    $col  = $as_colors[md5($name) % (count($as_colors) - 1)];
426    $col  = substr(md5($name),7,6);
427//    print "COL: $col<br>\n";
428
429    $opts[] = "DEF:v$id=$as_libdir/$id.rrd:hits:AVERAGE";
430    $opts[] = "CDEF:gv$id=v$id,$rate,*";
431//    $opts[] = "CDEF:gv$id=v$id,UN,0,v$id,IF,$rate,*";
432    $opts[] = "$type:gv$id#$col:$name $count";
433
434    return $opts;
435}
436
437
438/*
439* asVGraph (file,    - name of the png to generate
440*           endtime, - end time in seconds (defaults to 'now')
441*           length,  - length of time in seconds
442*           timetext,- human-readable description of length (eg: 'by day')
443*           hostname,- hostname of amavis server (defaults to localhost)
444*         )
445*
446* Build a graph of Virus infected emails.
447* Returns either a html-valid <img> tag which can be printed, or the
448* boolean "false" if something went wrong.
449*/
450function asVGraph($img, $end = 0, $length, $timetext = "", $host = "") {
451    global $as_libdir, $virus, $pid, $psid, $iid, $bid, $nid, $nsid, $lastupdate, $maxi, $rate, $asVersion, $rrdstep;
452
453    /*
454    * Options... when do we start, end, graph title text etc.
455    */
456    if ($end == 0) {
457        $end = time();
458    }
459
460    // make the end time an even multiple of the rrdstep
461    $end = floor($end/$rrdstep) * $rrdstep;
462
463
464    $start = $end - $length;
465
466    $startdate = strftime("%c", $start);
467    $enddate   = strftime("%c", $end);
468    $nowdate   = strftime("%c", time());
469
470
471    if ($timetext == "") {
472        $timetext = "$length seconds";
473    }
474
475    if ($host == "") {
476        $host = eregi_replace("\n", "", `hostname`);
477    }
478
479
480    /*
481    * It is a two-step process to build the final graph. The average over
482    * a specific time period seems to be impossible to get without actually
483    * building a graph. Ie, rrd fetch will not calculate the values we
484    * need - we would have to sum and average manually.
485    *
486    * However the PRINT function of a graph will return what we want
487    * in an array. So first of all build a graph that PRINTs the average
488    * of every virus over the selected time period.
489    */
490
491    $opts = array();
492    $opts[] = "--start=$start";
493    $opts[] = "--end=$end";
494
495    foreach ($virus as $id => $rest) {
496        $opts[] = "DEF:v$id=$as_libdir/$id.rrd:hits:AVERAGE";
497        $opts[] = "CDEF:gv$id=v$id,UN,0,v$id,IF";
498        $opts[] = "CDEF:gvt$id=gv$id,$length,*";
499        $opts[] = "PRINT:gvt$id:AVERAGE:%.0lf";
500    }
501
502    $ret = rrd_graph($img, $opts, count($opts));
503
504    /*
505    * debugging - graph definitions
506    */
507    asDbg($ret);
508
509    $infected = 0;
510    if (is_array($ret)) {
511        /*
512        * All results from PRINT commands are in the array $ret[calcpr][..]
513        */
514    	$maxi = 0;
515        $i = 0;
516        foreach ($virus as $id => $rest) {
517            /*
518            * We don't have enough resolution in the rrds
519            * to calculate the correct counts at low averages,
520            * so we just don't display them
521            */
522            if ($ret[calcpr][$i] != 0) {
523                $vcount[$id] = $ret[calcpr][$i];
524	        $maxi = max($maxi, strlen($virus[$id]["name"]));
525            }
526            $i++;
527            /* asDbg("Id: $id = $vcount[$id]"); */
528        }
529	    $maxi++;
530
531        /*
532        * We usually always have the infected.rrd and passed.rrd 'viruses'.
533        * Take them out of the array (saving the totals) because we don't
534        * really want to graph them.
535        */
536        if (count($vcount) >= 1) {
537            arsort($vcount);
538            if ($vcount[$iid]) {
539                $infected = $vcount[$iid];
540                unset ($vcount[$iid]);
541            }
542            if ($vcount[$pid]) {
543                unset ($vcount[$pid]);
544            }
545            if ($vcount[$psid]) {
546                unset ($vcount[$psid]);
547            }
548            if ($vcount[$bid]) {
549                unset ($vcount[$bid]);
550            }
551            if ($vcount[$nid]) {
552                unset ($vcount[$nid]);
553            }
554            if ($vcount[$nsid]) {
555                unset ($vcount[$nsid]);
556            }
557
558        }
559        else {
560            asDbg("vcount is an empty array");
561            $vcount = array();
562        }
563
564    }
565    else
566    {
567        $msg = rrd_error();
568        asErr("rrd_graph(): $msg");
569        return false;
570    }
571
572    if ($rate == 3600) {
573        $ratemsg = "hour";
574    } else {
575        $rate = 60;
576        $ratemsg = "min";
577    }
578
579
580    /*
581    * Now that we have the counts of each virus over the time period
582    * we can build the actual graph
583    */
584    $opts = array();
585    $opts[] = "--start=$start";
586    $opts[] = "--end=$end";
587    $opts[] = "--title=Virus Detection on $host ($timetext)";
588    $opts[] = "--width=520";
589    $opts[] = "--vertical-label=viruses/$ratemsg";
590//    $opts[] = "COMMENT:$infected viruses detected from $startdate\g";
591//    $opts[] = "COMMENT: to $enddate";
592//    $opts[] = "COMMENT:\\n";
593//    $opts[] = "COMMENT:\\n";
594
595
596    /*
597    * The tricky part, building rrd rows but ordering the elements by
598    * columns...
599    */
600
601    if ($maxi > 20) {
602        $width = 2;
603    } else {
604        $width = 3;
605    }
606    $total = count($vcount);
607    $depth = ceil($total / $width);
608
609    $mod = $total % $width;
610
611    if ($infected > 0) {
612
613        $keyarray = array_keys ($vcount);
614
615        for ($d = 1; $d <= $depth; $d++) {
616            for ($col = 1; $col <= $width; $col++) {
617
618                if ($col == 1) {
619                    $index = $d;
620                }
621                elseif ($d != $depth || $mod == 0 || $mod >= $col) {
622                    if (($mod == 0) || ($col - $mod) < 2) {
623                        $index = ($col - 1) * $depth + $d;
624                    } else {
625                        $index = $mod * $depth +
626                                 ($col - $mod - 1)*($depth - 1) + $d ;
627                    }
628                }
629                else {
630                    continue;
631                }
632
633                $id = $keyarray[$index - 1];
634
635                if ($d == 1 && $col == 1) {
636                    addopts($opts, "AREA", $id, $vcount, $virus, $length);
637                }
638                else {
639                    addopts($opts, "STACK", $id, $vcount, $virus, $length);
640                }
641            }
642            $opts[] = "COMMENT:\\n";
643        }
644    }
645
646    $opts[] = "COMMENT:\\n";
647    $opts[] = "COMMENT:amavis-stats v$asVersion ";
648    $opts[] = "COMMENT:$enddate \\r";
649
650    asDbg($opts);
651    $ret = rrd_graph($img, $opts, count($opts));
652
653    if (!is_array($ret)) {
654        $err = rrd_error();
655        asErr("rrd_graph(): $err");
656        return false;
657    }
658
659    return "<img src=\"$img\" alt=\"[image: $timetext]\">\n\n";
660}
661
662
663
664
665
666/*
667* asPGraph ( file,    - name of the png to generate
668*           endtime, - end time in seconds (defaults to 'now')
669*           length,  - length of time in seconds
670*           timetext,- human-readable description of length (eg: 'by day')
671*           hostname,- hostname of amavis server (defaults to localhost)
672*         )
673*
674* Build a graph of clean or "Passed" emails.
675* Returns either a html-valid <img> tag which can be printed, or the
676* boolean "false" if something went wrong.
677*/
678function asPGraph($img, $end = 0, $length, $timetext = "", $host = "") {
679    global $as_libdir, $virus, $pid, $psid, $iid, $bid, $nid, $nsid, $lastupdate, $maxi, $rate, $asVersion, $rrdstep;
680
681    /*
682    * Options... when do we start, end, graph title text etc.
683    */
684    if ($end == 0) {
685        $end = time();
686    }
687
688    // make the end time an even multiple of the rrdstep
689    $end = floor($end/$rrdstep) * $rrdstep;
690    $start = $end - $length;
691
692    $startdate = strftime("%c", $start);
693    $enddate   = strftime("%c", $end);
694    $nowdate   = strftime("%c", time());
695
696    if ($timetext == "") {
697        $timetext = "$length seconds";
698    }
699
700    if ($host == "") {
701        $host = eregi_replace("\n", "", `hostname`);
702    }
703
704    if ($rate == 3600) {
705        $ratemsg = "hour";
706    } else {
707        $rate = 60;
708        $ratemsg = "min";
709    }
710
711    $opts = array();
712    $opts[] = "--start=$start";
713    $opts[] = "--end=$end";
714    $opts[] = "--title=Messages Scanned on $host ($timetext)";
715    $opts[] = "--width=520";
716    $opts[] = "--vertical-label=msgs/$ratemsg";
717
718    $arr = array($pid, $psid, $bid, $iid, $nid, $nsid);
719    $arrcol = array("6A5ACD", "fA5ACD", "cc9900", "ffdd00", "9900aa", "ff3a3d");
720
721    $type = "AREA";
722    foreach ($arr as $idx => $id) {
723        if ($id > 0) {
724            $name = sprintf("%-21s", $virus[$id]["name"]);
725            $col  = $arrcol[$idx];
726//            print "$col<br>\n";
727            $opts[] = "DEF:v$id=$as_libdir/$id.rrd:hits:AVERAGE";
728            $opts[] = "CDEF:gv$id=v$id,UN,0,v$id,IF";
729            $opts[] = "CDEF:gvt$id=gv$id,$length,*";
730            $opts[] = "CDEF:gtvt$id=gv$id,$rate,*";
731            $opts[] = "$type:gtvt$id#$col:$name ";
732            $opts[] = "GPRINT:gvt$id:AVERAGE:%.0lf";
733            $opts[] = "COMMENT:\\n";
734            $type = "STACK";
735        }
736    }
737/*
738
739    if ($iid >= 0) {
740        $opts[] = "DEF:v$iid=$as_libdir/$iid.rrd:hits:AVERAGE";
741        $opts[] = "CDEF:gv$iid=v$iid,UN,0,v$iid,IF";
742        $opts[] = "CDEF:gvt$iid=gv$iid,$length,*";
743        $opts[] = "CDEF:gtvt$iid=gv$iid,$rate,*";
744        $opts[] = "$type:gtvt$iid#FFD700:Infected\:   ";
745        $opts[] = "GPRINT:gvt$iid:AVERAGE:%.0lf";
746        $opts[] = "COMMENT:\\n";
747        $type = "STACK";
748    }
749
750    if ($bid >= 0) {
751        $opts[] = "DEF:v$bid=$as_libdir/$bid.rrd:hits:AVERAGE";
752        $opts[] = "CDEF:gv$bid=v$bid,UN,0,v$bid,IF";
753        $opts[] = "CDEF:gvt$bid=gv$bid,$length,*";
754        $opts[] = "CDEF:gtvt$bid=gv$bid,$rate,*";
755        $opts[] = "$type:gtvt$bid#FFA500:Banned\:     ";
756        $opts[] = "GPRINT:gvt$bid:AVERAGE:%.0lf";
757        $opts[] = "COMMENT:\\n";
758        $type = "STACK";
759    }
760
761    if ($sid >= 0) {
762        $opts[] = "DEF:v$sid=$as_libdir/$sid.rrd:hits:AVERAGE";
763        $opts[] = "CDEF:gv$sid=v$sid,UN,0,v$sid,IF";
764        $opts[] = "CDEF:gvt$sid=gv$sid,$length,*";
765        $opts[] = "CDEF:gtvt$sid=gv$sid,$rate,*";
766        $opts[] = "$type:gtvt$sid#FF1493:Spam\:       ";
767        $opts[] = "GPRINT:gvt$sid:AVERAGE:%.0lf";
768        $opts[] = "COMMENT:\\n";
769    }
770*/
771    $opts[] = "COMMENT:amavis-stats v$asVersion ";
772    $opts[] = "COMMENT:$enddate \\r";
773
774    /*
775    * debugging - graph definitions
776    */
777    asDbg($opts);
778
779    $start = microtime();
780    $ret = rrd_graph($img, $opts, count($opts));
781    $t = elapsed($start);
782    if (!is_array($ret)) {
783        $err = rrd_error();
784        asErr("rrd_graph(): $err");
785        return false;
786    }
787
788    return "<img src=\"$img\" alt=\"[image: $timetext]\">\n\n";
789}
790
791
792?>
793