1 #include "common.h"
2 #include "dbsql.h"
3 #include "misc.h"
4 #include "image.h"
5 #include "image_support.h"
6 #include "vnstati.h"
7
initimagecontent(IMAGECONTENT * ic)8 void initimagecontent(IMAGECONTENT *ic)
9 {
10 ic->im = NULL;
11 ic->font = gdFontGetSmall();
12 ic->lineheight = 12;
13 ic->large = 0;
14 ic->showheader = 1;
15 ic->showedge = 1;
16 ic->showlegend = 1;
17 ic->altdate = 0;
18 ic->headertext[0] = '\0';
19 ic->databegin[0] = '\0';
20 ic->dataend[0] = '\0';
21 ic->interface.name[0] = '\0';
22 ic->interface.alias[0] = '\0';
23 }
24
drawimage(IMAGECONTENT * ic)25 void drawimage(IMAGECONTENT *ic)
26 {
27 switch (cfg.qmode) {
28 case 1:
29 drawlist(ic, "day");
30 break;
31 case 2:
32 drawlist(ic, "month");
33 break;
34 case 3:
35 drawlist(ic, "top");
36 break;
37 case 4:
38 drawlist(ic, "year");
39 break;
40 case 5:
41 drawsummary(ic, 0, 0);
42 break;
43 case 51:
44 drawsummary(ic, 1, cfg.hourlyrate); // horizontal
45 break;
46 case 52:
47 drawsummary(ic, 2, cfg.hourlyrate); // vertical
48 break;
49 case 7:
50 drawhourly(ic, cfg.hourlyrate);
51 break;
52 case 8:
53 drawlist(ic, "hour");
54 break;
55 case 9:
56 drawlist(ic, "fiveminute");
57 break;
58 case 10:
59 drawfivegraph(ic, cfg.hourlyrate, cfg.fivegresultcount, cfg.fivegheight);
60 break;
61 default:
62 printf("Error: No such query mode: %d\n", cfg.qmode);
63 exit(EXIT_FAILURE);
64 }
65
66 /* enable background transparency if needed */
67 if (cfg.transbg) {
68 gdImageColorTransparent(ic->im, ic->cbackground);
69 }
70 }
71
72 #if HAVE_DECL_GD_NEAREST_NEIGHBOUR
scaleimage(IMAGECONTENT * ic)73 void scaleimage(IMAGECONTENT *ic)
74 {
75 gdImagePtr im_scaled;
76 unsigned int width = 0, height = 0;
77
78 if (cfg.imagescale == 100 || ic->im == NULL) {
79 return;
80 }
81
82 width = (unsigned int)((float)gdImageSX(ic->im) * ((float)cfg.imagescale / (float)100));
83 height = (unsigned int)((float)gdImageSY(ic->im) * ((float)cfg.imagescale / (float)100));
84
85 if (width < 100 || height < 100) {
86 return;
87 }
88
89 if (width > 5000 || height > 5000) {
90 return;
91 }
92
93 /* keep output sharp when percent is an exact multiplier */
94 if (cfg.imagescale % 100 == 0) {
95 gdImageSetInterpolationMethod(ic->im, GD_NEAREST_NEIGHBOUR);
96 }
97
98 im_scaled = gdImageScale(ic->im, width, height);
99 if (im_scaled == NULL) {
100 return;
101 }
102
103 gdImageDestroy(ic->im);
104 ic->im = im_scaled;
105 }
106 #endif
107
drawhours(IMAGECONTENT * ic,const int xpos,const int ypos,const int rate)108 int drawhours(IMAGECONTENT *ic, const int xpos, const int ypos, const int rate)
109 {
110 int i, tmax = 0, s = 0, step, prev = 0, diff = 0, chour;
111 int x = xpos, y = ypos, extrax = 0, extray = 0;
112 double ratediv;
113 uint64_t max = 1, scaleunit = 0;
114 char buffer[32];
115 struct tm *d;
116 dbdatalist *datalist = NULL, *datalist_i = NULL;
117 dbdatalistinfo datainfo;
118 HOURDATA hourdata[24];
119 gdFontPtr font;
120
121 if (ic->large) {
122 font = gdFontGetSmall();
123 } else {
124 font = gdFontGetTiny();
125 }
126
127 for (i = 0; i < 24; i++) {
128 hourdata[i].rx = hourdata[i].tx = 0;
129 hourdata[i].date = 0;
130 }
131
132 if (!db_getdata(&datalist, &datainfo, ic->interface.name, "hour", 24) || datainfo.count == 0) {
133 gdImageString(ic->im, ic->font, x + (32 * ic->font->w), y + 54, (unsigned char *)"no data available", ic->ctext);
134 return 0;
135 }
136
137 datalist_i = datalist;
138
139 while (datalist_i != NULL) {
140 d = localtime(&datalist_i->timestamp);
141 if (hourdata[d->tm_hour].date != 0 || ic->interface.updated - datalist_i->timestamp > 86400) {
142 datalist_i = datalist_i->next;
143 continue;
144 }
145 hourdata[d->tm_hour].rx = datalist_i->rx;
146 hourdata[d->tm_hour].tx = datalist_i->tx;
147 hourdata[d->tm_hour].date = datalist_i->timestamp;
148 datalist_i = datalist_i->next;
149 }
150 dbdatalistfree(&datalist);
151
152 ic->current = ic->interface.updated;
153 chour = localtime(&ic->current)->tm_hour;
154
155 if (cfg.rateunit) {
156 ratediv = 450; /* x * 8 / 3600 */
157 } else {
158 ratediv = 3600;
159 }
160
161 /* tmax (time max) = current hour */
162 /* max = transfer max */
163
164 for (i = 0; i < 24; i++) {
165 /* convert hourly transfer to hourly rate if needed */
166 if (rate) {
167 if ((ic->current - hourdata[i].date) > 3600) {
168 hourdata[i].rx = (uint64_t)((double)hourdata[i].rx / ratediv);
169 hourdata[i].tx = (uint64_t)((double)hourdata[i].tx / ratediv);
170 } else {
171 /* scale ongoing hour properly */
172 if (chour != i) {
173 hourdata[i].rx = (uint64_t)((double)hourdata[i].rx / ratediv);
174 hourdata[i].tx = (uint64_t)((double)hourdata[i].tx / ratediv);
175 } else {
176 d = localtime(&ic->current);
177 diff = d->tm_min * 60;
178 if (!diff) {
179 diff = 60;
180 }
181 if (cfg.rateunit == 1) {
182 hourdata[i].rx *= 8;
183 hourdata[i].tx *= 8;
184 }
185 hourdata[i].rx = (uint64_t)((double)hourdata[i].rx / (double)diff);
186 hourdata[i].tx = (uint64_t)((double)hourdata[i].tx / (double)diff);
187 }
188 }
189 }
190
191 if (hourdata[i].date >= hourdata[tmax].date) {
192 tmax = i;
193 }
194 if (hourdata[i].rx >= max) {
195 max = hourdata[i].rx;
196 }
197 if (hourdata[i].tx >= max) {
198 max = hourdata[i].tx;
199 }
200 }
201
202 if (ic->large) {
203 x += 14;
204 extrax = 145;
205 extray = 35;
206 }
207
208 /* scale values */
209 scaleunit = getscale(max, rate);
210
211 s = (int)lrint(((double)scaleunit / (double)max) * (124 + extray));
212 if (s < SCALEMINPIXELS) {
213 step = 2;
214 } else {
215 step = 1;
216 }
217
218 for (i = step; i * s <= (124 + extray + 4); i = i + step) {
219 gdImageDashedLine(ic->im, x + 36, y + 124 - (i * s), x + 460 + extrax, y + 124 - (i * s), ic->cline);
220 gdImageDashedLine(ic->im, x + 36, y + 124 - prev - (step * s) / 2, x + 460 + extrax, y + 124 - prev - (step * s) / 2, ic->clinel);
221 gdImageString(ic->im, font, x + 16 - (ic->large * 3), y + 121 - (i * s) - (ic->large * 3), (unsigned char *)getimagevalue(scaleunit * (unsigned int)i, 3, rate), ic->ctext);
222 prev = i * s;
223 }
224 if ((prev + (step * s) / 2) <= (124 + extray + 4)) {
225 gdImageDashedLine(ic->im, x + 36, y + 124 - prev - (step * s) / 2, x + 460 + extrax, y + 124 - prev - (step * s) / 2, ic->clinel);
226 }
227
228 /* scale text */
229 gdImageStringUp(ic->im, font, x - 2 - (ic->large * 14), y + 58 + (rate * 10) - (extray / 2), (unsigned char *)getimagescale(scaleunit * (unsigned int)step, rate), ic->ctext);
230
231 /* axis */
232 gdImageLine(ic->im, x + 36 - 4, y + 124, x + 466 + extrax, y + 124, ic->ctext);
233 gdImageLine(ic->im, x + 36, y - 10 - extray, x + 36, y + 124 + 4, ic->ctext);
234
235 /* arrows */
236 drawarrowup(ic, x + 36, y - 9 - extray);
237 drawarrowright(ic, x + 465 + extrax, y + 124);
238
239 /* x-axis values and poles */
240 for (i = 0; i < 24; i++) {
241 s = tmax - i;
242 if (s < 0) {
243 s += 24;
244 }
245 snprintf(buffer, 32, "%02d ", s);
246 if (hourdata[s].date == 0) {
247 chour = ic->cline;
248 } else {
249 chour = ic->ctext;
250 }
251 gdImageString(ic->im, font, x + 440 - (i * (17 + ic->large * 6)) + extrax, y + 128, (unsigned char *)buffer, chour);
252 drawpoles(ic, x + 438 - (i * (17 + ic->large * 6)) + extrax, y - extray, 124 + extray, hourdata[s].rx, hourdata[s].tx, max);
253 gdImageLine(ic->im, x + 438 - 2 - (i * (17 + ic->large * 6)) + extrax, y + 124, x + 438 + 14 - (i * (17 + ic->large * 6)) + extrax, y + 124, chour);
254 }
255
256 return 1;
257 }
258
drawhourly(IMAGECONTENT * ic,const int rate)259 void drawhourly(IMAGECONTENT *ic, const int rate)
260 {
261 int width, height, headermod = 0;
262
263 width = 500 + (ic->large * 168);
264 height = 200 + (ic->large * 48);
265
266 if (!ic->showheader) {
267 headermod = 26;
268 height -= 22;
269 }
270
271 imageinit(ic, width, height);
272 layoutinit(ic, " / hourly", width, height);
273
274 if (drawhours(ic, 12, 46 - headermod + (ic->large * 40), rate)) {
275 drawlegend(ic, width / 2 - (ic->large * 10), 183 - headermod + (ic->large * 46), 0);
276 }
277 }
278
drawlist(IMAGECONTENT * ic,const char * listname)279 void drawlist(IMAGECONTENT *ic, const char *listname)
280 {
281 ListType listtype = LT_None;
282 int textx, texty, offsetx = 0;
283 int width, height, headermod, i = 1, rowcount = 0;
284 int estimateavailable = 0, estimatevisible = 0;
285 int32_t limit;
286 uint64_t e_rx = 0, e_tx = 0, e_secs = 0;
287 char buffer[512], datebuff[16], daybuff[16];
288 char stampformat[64], titlename[16], colname[8];
289 struct tm *d;
290 time_t current;
291 dbdatalist *datalist = NULL, *datalist_i = NULL;
292 dbdatalistinfo datainfo;
293
294 if (strcmp(listname, "day") == 0) {
295 listtype = LT_Day;
296 strncpy_nt(colname, listname, 8);
297 snprintf(titlename, 16, "daily");
298 strncpy_nt(stampformat, cfg.dformat, 64);
299 limit = cfg.listdays;
300 } else if (strcmp(listname, "month") == 0) {
301 listtype = LT_Month;
302 strncpy_nt(colname, listname, 8);
303 snprintf(titlename, 16, "monthly");
304 strncpy_nt(stampformat, cfg.mformat, 64);
305 limit = cfg.listmonths;
306 } else if (strcmp(listname, "year") == 0) {
307 listtype = LT_Year;
308 strncpy_nt(colname, listname, 8);
309 snprintf(titlename, 16, "yearly");
310 strncpy_nt(stampformat, "%Y", 64);
311 limit = cfg.listyears;
312 } else if (strcmp(listname, "top") == 0) {
313 listtype = LT_Top;
314 snprintf(colname, 8, "day");
315 strncpy_nt(stampformat, cfg.tformat, 64);
316 limit = cfg.listtop;
317 offsetx = 5 * ic->font->w;
318 } else if (strcmp(listname, "hour") == 0) {
319 listtype = LT_Hour;
320 strncpy_nt(colname, listname, 8);
321 snprintf(titlename, 16, "hourly");
322 strncpy_nt(stampformat, "%H:%M", 64);
323 limit = cfg.listhours;
324 } else if (strcmp(listname, "fiveminute") == 0) {
325 listtype = LT_5min;
326 strncpy_nt(colname, "time", 8);
327 snprintf(titlename, 16, "5 minute");
328 strncpy_nt(stampformat, "%H:%M", 64);
329 limit = cfg.listfivemins;
330 } else {
331 return;
332 }
333
334 if (limit < 0) {
335 limit = 0;
336 }
337
338 daybuff[0] = '\0';
339
340 db_getdata_range(&datalist, &datainfo, ic->interface.name, listname, (uint32_t)limit, ic->databegin, ic->dataend);
341
342 datalist_i = datalist;
343
344 if (strlen(ic->dataend) == 0 && datainfo.count > 0 && listtype != LT_Top) {
345 getestimates(&e_rx, &e_tx, listtype, ic->interface.updated, &datalist);
346 if ((cfg.estimatestyle > 0 || cfg.barshowsrate > 0) && e_rx + e_tx > datainfo.max) {
347 datainfo.max = e_rx + e_tx;
348 }
349 estimateavailable = 1;
350 if (listtype == LT_Day || listtype == LT_Month || listtype == LT_Year) {
351 estimatevisible = 1;
352 }
353 }
354
355 if (listtype == LT_Top) {
356 if (limit > 0 && datainfo.count < (uint32_t)limit) {
357 limit = (int32_t)datainfo.count;
358 }
359 if (limit <= 0 || datainfo.count > 999) {
360 snprintf(titlename, 16, "top");
361 } else {
362 snprintf(titlename, 16, "top %d", limit);
363 }
364 }
365
366 if (listtype == LT_Hour || listtype == LT_5min) {
367 while (datalist_i != NULL) {
368 d = localtime(&datalist_i->timestamp);
369 strftime(datebuff, 16, cfg.dformat, d);
370 if (strcmp(daybuff, datebuff) != 0) {
371 rowcount += 1;
372 strcpy(daybuff, datebuff);
373 }
374 datalist_i = datalist_i->next;
375 }
376 datalist_i = datalist;
377 daybuff[0] = '\0';
378 }
379 rowcount += datainfo.count;
380
381 width = 83 * ic->font->w + 2 + (ic->large * 2);
382 height = 62 + 3 * ic->lineheight;
383
384 // less space needed when no estimate or sum is shown (Top, 5min and Hour never have estimate)
385 if ((!estimatevisible && datainfo.count < 2) || (listtype == LT_Top || listtype == LT_Hour || listtype == LT_5min)) {
386 height = 62 + 2 * ic->lineheight;
387 }
388
389 // exception for 5min and Hour when having sum shown
390 if ((listtype == LT_5min || listtype == LT_Hour) && datainfo.count > 1 && strlen(ic->dataend) > 0) {
391 height = 62 + 3 * ic->lineheight;
392 }
393
394 height += (ic->lineheight + cfg.linespaceadjust) * rowcount - cfg.linespaceadjust;
395
396 // "no data available"
397 if (!datainfo.count) {
398 height = 98 + (ic->large * 12);
399 }
400
401 if (!ic->showheader) {
402 headermod = 26;
403 height -= 22;
404 } else {
405 headermod = 0;
406 }
407
408 snprintf(buffer, 512, " / %s", titlename);
409
410 imageinit(ic, width, height);
411 layoutinit(ic, buffer, width, height);
412
413 if (datainfo.count) {
414 if (listtype == LT_Top) {
415 if (cfg.ostyle <= 2) {
416 drawlegend(ic, 66 * ic->font->w + 2, 40 - headermod, 0);
417 }
418 current = time(NULL);
419 d = localtime(¤t);
420 strftime(daybuff, 16, stampformat, d);
421 } else { // everything else
422 if (cfg.ostyle > 2) {
423 if (estimateavailable && cfg.barshowsrate) {
424 drawlegend(ic, 72 * ic->font->w, 40 - headermod, 1);
425 } else {
426 drawlegend(ic, 72 * ic->font->w, 40 - headermod, 0);
427 }
428 } else {
429 drawlegend(ic, 64 * ic->font->w + 1, 40 - headermod, 0);
430 }
431 }
432 }
433
434 textx = 10;
435 texty = 40 - headermod;
436
437 if (listtype == LT_Top) { // top
438 snprintf(buffer, 512, " # day rx tx total");
439 } else { // everything else
440 snprintf(buffer, 512, " %8s rx tx total", colname);
441 }
442 if (cfg.ostyle > 2) {
443 strcat(buffer, " avg. rate");
444 gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext);
445 gdImageLine(ic->im, textx + 2, texty + ic->lineheight + 4, textx + (65 * ic->font->w) + offsetx + 2, texty + ic->lineheight + 4, ic->cline);
446 } else {
447 gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext);
448 gdImageLine(ic->im, textx + 2, texty + ic->lineheight + 4, textx + (50 * ic->font->w) + offsetx - 4, texty + ic->lineheight + 4, ic->cline);
449 }
450
451 texty += ic->lineheight + 8;
452
453 if (datainfo.count) {
454 gdImageLine(ic->im, textx + (24 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (24 * ic->font->w) + offsetx, texty + ((ic->lineheight + cfg.linespaceadjust) * rowcount) - cfg.linespaceadjust + 5 - (ic->large * 2), ic->cline);
455 gdImageLine(ic->im, textx + (37 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (37 * ic->font->w) + offsetx, texty + ((ic->lineheight + cfg.linespaceadjust) * rowcount) - cfg.linespaceadjust + 5 - (ic->large * 2), ic->cline);
456 if (cfg.ostyle > 2) {
457 gdImageLine(ic->im, textx + (50 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (50 * ic->font->w) + offsetx, texty + ((ic->lineheight + cfg.linespaceadjust) * rowcount) - cfg.linespaceadjust + 5 - (ic->large * 2), ic->cline);
458 }
459 } else {
460 gdImageLine(ic->im, textx + (24 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (24 * ic->font->w) + offsetx, texty - 4, ic->cline);
461 gdImageLine(ic->im, textx + (37 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (37 * ic->font->w) + offsetx, texty - 4, ic->cline);
462 if (cfg.ostyle > 2) {
463 gdImageLine(ic->im, textx + (50 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (50 * ic->font->w) + offsetx, texty - 4, ic->cline);
464 }
465 }
466
467 while (datalist_i != NULL) {
468 d = localtime(&datalist_i->timestamp);
469
470 if (listtype == LT_5min || listtype == LT_Hour) {
471 strftime(datebuff, 16, cfg.dformat, d);
472 if (strcmp(daybuff, datebuff) != 0) {
473 snprintf(buffer, 32, " %s", datebuff);
474 gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext);
475 texty += ic->lineheight + cfg.linespaceadjust;
476 strcpy(daybuff, datebuff);
477 }
478 }
479
480 if (listtype == LT_Top) {
481 if (strftime(datebuff, 16, stampformat, d) <= 8) {
482 snprintf(buffer, 32, " %2d %*s", i, getpadding(8, datebuff), datebuff);
483 strcat(buffer, " ");
484 } else {
485 snprintf(buffer, 32, " %2d %-*s ", i, getpadding(11, datebuff), datebuff);
486 }
487 if (strcmp(datebuff, daybuff) == 0) {
488 if (cfg.ostyle > 2) {
489 gdImageFilledRectangle(ic->im, textx + 2, texty + 2 - (ic->large * 1), textx + (65 * ic->font->w) + offsetx + 2, texty + 11 + (ic->large * 3), ic->cbgoffset);
490 } else {
491 gdImageFilledRectangle(ic->im, textx + 2, texty + 2 - (ic->large * 1), textx + (50 * ic->font->w) + offsetx - 4, texty + 11 + (ic->large * 3), ic->cbgoffset);
492 }
493 }
494 } else {
495 if (strftime(datebuff, 16, stampformat, d) <= 8) {
496 snprintf(buffer, 32, " %*s", getpadding(8, datebuff), datebuff);
497 strcat(buffer, " ");
498 } else {
499 snprintf(buffer, 32, " %-*s ", getpadding(11, datebuff), datebuff);
500 }
501 }
502 strncat(buffer, getvalue(datalist_i->rx, 10, RT_Normal), 32);
503 strcat(buffer, " ");
504 strncat(buffer, getvalue(datalist_i->tx, 10, RT_Normal), 32);
505 strcat(buffer, " ");
506 strncat(buffer, getvalue(datalist_i->rx + datalist_i->tx, 10, RT_Normal), 32);
507 if (cfg.ostyle > 2) {
508 strcat(buffer, " ");
509 if (datalist_i->next == NULL && issametimeslot(listtype, datalist_i->timestamp, ic->interface.updated)) {
510 e_secs = getperiodseconds(listtype, datalist_i->timestamp, ic->interface.updated, 1);
511 } else {
512 e_secs = getperiodseconds(listtype, datalist_i->timestamp, ic->interface.updated, 0);
513 }
514 strncat(buffer, gettrafficrate(datalist_i->rx + datalist_i->tx, (time_t)e_secs, 14), 32);
515 }
516 gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext);
517 if (listtype == LT_Top) {
518 if (cfg.ostyle > 2) {
519 drawbar(ic, textx + (71 * ic->font->w) + 2, texty + 4, 9 * ic->font->w - 1, datalist_i->rx, datalist_i->tx, datainfo.max, 0);
520 } else {
521 drawbar(ic, textx + (56 * ic->font->w), texty + 4, 23 * ic->font->w + 3, datalist_i->rx, datalist_i->tx, datainfo.max, 0);
522 }
523 } else { // everything else
524 if (cfg.ostyle > 2) {
525 if (datalist_i->next == NULL && estimateavailable && cfg.barshowsrate) {
526 drawbar(ic, textx + (67 * ic->font->w) - 2, texty + 4, 13 * ic->font->w + 1, e_rx, e_tx, datainfo.max, 0);
527 } else {
528 drawbar(ic, textx + (67 * ic->font->w) - 2, texty + 4, 13 * ic->font->w + 1, datalist_i->rx, datalist_i->tx, datainfo.max, 0);
529 }
530 } else {
531 drawbar(ic, textx + (51 * ic->font->w) - 2, texty + 4, 28 * ic->font->w + 3, datalist_i->rx, datalist_i->tx, datainfo.max, 0);
532 }
533 }
534 texty += ic->lineheight + cfg.linespaceadjust;
535 if (datalist_i->next == NULL) {
536 texty -= cfg.linespaceadjust;
537 break;
538 }
539 datalist_i = datalist_i->next;
540 i++;
541 }
542
543 if (!datainfo.count) {
544 i = 17 * ic->font->w;
545 if (cfg.ostyle > 2) {
546 i += 8 * ic->font->w;
547 }
548 gdImageString(ic->im, ic->font, textx + i, texty, (unsigned char *)"no data available", ic->ctext);
549 texty += ic->lineheight;
550 }
551
552 if (cfg.ostyle > 2) {
553 gdImageLine(ic->im, textx + 2, texty + 5 - (ic->large * 2), textx + (65 * ic->font->w) + offsetx + 2, texty + 5 - (ic->large * 2), ic->cline);
554 } else {
555 gdImageLine(ic->im, textx + 2, texty + 5 - (ic->large * 2), textx + (50 * ic->font->w) + offsetx - 4, texty + 5 - (ic->large * 2), ic->cline);
556 }
557
558 buffer[0] = '\0';
559
560 /* estimate visible */
561 if (estimatevisible) {
562 snprintf(buffer, 32, " estimated ");
563 strncat(buffer, getvalue(e_rx, 10, RT_Estimate), 32);
564 strcat(buffer, " ");
565 strncat(buffer, getvalue(e_tx, 10, RT_Estimate), 32);
566 strcat(buffer, " ");
567 strncat(buffer, getvalue(e_rx + e_tx, 10, RT_Estimate), 32);
568
569 if (cfg.estimatestyle) {
570 if (cfg.ostyle > 2) {
571 drawbar(ic, textx + (67 * ic->font->w) - 2, texty - ic->lineheight + 4, 13 * ic->font->w + 1, e_rx, e_tx, datainfo.max, 1);
572 drawbar(ic, textx + (67 * ic->font->w) - 2, texty - ic->lineheight + 4, 13 * ic->font->w + 1, datalist_i->rx, datalist_i->tx, datainfo.max, 0);
573 } else {
574 drawbar(ic, textx + (51 * ic->font->w) - 2, texty - ic->lineheight + 4, 28 * ic->font->w + 3, e_rx, e_tx, datainfo.max, 1);
575 drawbar(ic, textx + (51 * ic->font->w) - 2, texty - ic->lineheight + 4, 28 * ic->font->w + 3, datalist_i->rx, datalist_i->tx, datainfo.max, 0);
576 }
577 }
578
579 /* sum visible */
580 } else if (strlen(ic->dataend) > 0 && datainfo.count > 1 && listtype != LT_Top) {
581 if (datainfo.count < 100) {
582 snprintf(datebuff, 16, "sum of %" PRIu32 "", datainfo.count);
583 } else {
584 snprintf(datebuff, 16, "sum");
585 }
586 snprintf(buffer, 32, " %9s ", datebuff);
587 strncat(buffer, getvalue(datainfo.sumrx, 10, RT_Normal), 32);
588 strcat(buffer, " ");
589 strncat(buffer, getvalue(datainfo.sumtx, 10, RT_Normal), 32);
590 strcat(buffer, " ");
591 strncat(buffer, getvalue(datainfo.sumrx + datainfo.sumtx, 10, RT_Normal), 32);
592 }
593
594 if (strlen(buffer) > 0) {
595 texty += 8;
596 gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext);
597
598 gdImageLine(ic->im, textx + (24 * ic->font->w) + offsetx, texty - 6, textx + (24 * ic->font->w) + offsetx, texty + ic->lineheight - (ic->large * 2), ic->cline);
599 gdImageLine(ic->im, textx + (37 * ic->font->w) + offsetx, texty - 6, textx + (37 * ic->font->w) + offsetx, texty + ic->lineheight - (ic->large * 2), ic->cline);
600 if (cfg.ostyle > 2) {
601 gdImageLine(ic->im, textx + (50 * ic->font->w) + offsetx, texty - 6, textx + (50 * ic->font->w) + offsetx, texty + ic->lineheight - (ic->large * 2), ic->cline);
602 }
603 }
604
605 dbdatalistfree(&datalist);
606 }
607
drawsummary(IMAGECONTENT * ic,const int layout,const int rate)608 void drawsummary(IMAGECONTENT *ic, const int layout, const int rate)
609 {
610 int width, height, headermod;
611
612 switch (layout) {
613 // horizontal
614 case 1:
615 width = 163 * ic->font->w + 2 + (ic->large * 2);
616 height = 56 + 12 * ic->lineheight;
617 break;
618 // vertical
619 case 2:
620 width = 83 * ic->font->w + 2 + (ic->large * 2);
621 height = 370 + (ic->large * 90);
622 break;
623 // no hours
624 default:
625 width = 83 * ic->font->w + 2 + (ic->large * 2);
626 height = 56 + 12 * ic->lineheight;
627 break;
628 }
629
630 if (!ic->showheader) {
631 headermod = 26;
632 height -= 22;
633 } else {
634 headermod = 0;
635 }
636
637 imageinit(ic, width, height);
638 layoutinit(ic, "", width, height);
639
640 if (ic->interface.rxtotal == 0 && ic->interface.txtotal == 0) {
641 gdImageString(ic->im, ic->font, 33 * ic->font->w, 100, (unsigned char *)"no data available", ic->ctext);
642 return;
643 }
644
645 drawsummary_alltime(ic, 385 + (ic->large * 125), 57 - headermod + (ic->large * 10));
646 drawlegend(ic, 410 + (ic->large * 132), 155 - headermod + (ic->large * 40), 0);
647
648 drawsummary_digest(ic, 100, 30 - headermod, "day");
649 drawsummary_digest(ic, 100, 29 + 7 * ic->lineheight - headermod, "month");
650
651 switch (layout) {
652 // horizontal
653 case 1:
654 if (cfg.summarygraph == 1) {
655 drawfiveminutes(ic, 496 + (ic->large * 174), height - 30 - (ic->large * 8), rate, 422 + (ic->large * 154), height - 68 + headermod - (ic->large * 8));
656 } else {
657 drawhours(ic, 500 + (ic->large * 160), 46 + (ic->large * 40) - headermod, rate);
658 }
659 break;
660 // vertical
661 case 2:
662 if (cfg.summarygraph == 1) {
663 drawfiveminutes(ic, 8 + (ic->large * 14), height - 31 - (ic->large * 6), rate, 422 + (ic->large * 154), 132 + (ic->large * 35));
664 } else {
665 drawhours(ic, 12, 215 + (ic->large * 84) - headermod, rate);
666 }
667 break;
668 default:
669 break;
670 }
671 }
672
drawsummary_alltime(IMAGECONTENT * ic,const int x,const int y)673 void drawsummary_alltime(IMAGECONTENT *ic, const int x, const int y)
674 {
675 struct tm *d;
676 char buffer[512], datebuff[16], daytemp[32];
677 gdFontPtr titlefont;
678
679 if (ic->large) {
680 titlefont = gdFontGetGiant();
681 } else {
682 titlefont = gdFontGetLarge();
683 }
684
685 gdImageString(ic->im, titlefont, x + 12 + (ic->large * 10), y, (unsigned char *)"all time", ic->ctext);
686 snprintf(buffer, 4, "rx ");
687 strncat(buffer, getvalue(ic->interface.rxtotal, 12, RT_Normal), 32);
688 gdImageString(ic->im, ic->font, x, y + (2 * ic->lineheight), (unsigned char *)buffer, ic->ctext);
689 snprintf(buffer, 4, "tx ");
690 strncat(buffer, getvalue(ic->interface.txtotal, 12, RT_Normal), 32);
691 gdImageString(ic->im, ic->font, x, y + (3 * ic->lineheight), (unsigned char *)buffer, ic->ctext);
692 snprintf(buffer, 4, " = ");
693 strncat(buffer, getvalue(ic->interface.rxtotal + ic->interface.txtotal, 12, RT_Normal), 32);
694 gdImageString(ic->im, ic->font, x, y + (4 * ic->lineheight) + 2 + (ic->large * 4), (unsigned char *)buffer, ic->ctext);
695 d = localtime(&ic->interface.created);
696 strftime(datebuff, 16, cfg.tformat, d);
697 snprintf(daytemp, 24, "since %s", datebuff);
698 snprintf(buffer, 32, "%23s", daytemp);
699 gdImageString(ic->im, ic->font, x - 8 * ic->font->w, y + (5 * ic->lineheight) + 10 + (ic->large * 4), (unsigned char *)buffer, ic->ctext);
700 }
701
drawsummary_digest(IMAGECONTENT * ic,const int x,const int y,const char * mode)702 void drawsummary_digest(IMAGECONTENT *ic, const int x, const int y, const char *mode)
703 {
704 int textx, texty, offset = 0;
705 double rxp, txp, mod;
706 char buffer[512], datebuff[16], daytemp[32];
707 time_t yesterday;
708 struct tm *d = NULL;
709 dbdatalist *datalist = NULL;
710 dbdatalist *data_current = NULL, *data_previous = NULL;
711 dbdatalistinfo datainfo;
712 gdFontPtr titlefont;
713
714 if (ic->large) {
715 titlefont = gdFontGetGiant();
716 } else {
717 titlefont = gdFontGetLarge();
718 }
719
720 yesterday = ic->current - 86400;
721
722 switch(mode[0]) {
723 case 'd':
724 break;
725 case 'm':
726 break;
727 default:
728 printf("Error: Unsupported mode %s for summary digest\n", mode);
729 return;
730 }
731
732 if (!db_getdata(&datalist, &datainfo, ic->interface.name, mode, 2) || datalist == NULL) {
733 gdImageString(ic->im, ic->font, 25 * ic->font->w, y + 30, (unsigned char *)"no data available", ic->ctext);
734 return;
735 } else if (datalist->next == NULL) {
736 data_current = datalist;
737 } else {
738 data_previous = datalist;
739 data_current = datalist->next;
740 }
741
742 /* latest entry */
743 if (data_current->rx + data_current->tx == 0) {
744 rxp = txp = 0;
745 } else {
746 rxp = (double)data_current->rx / (double)(data_current->rx + data_current->tx) * 100;
747 txp = (double)100 - rxp;
748 }
749
750 /* do scaling if needed */
751 if (data_previous != NULL && (data_current->rx + data_current->tx) < (data_previous->rx + data_previous->tx)) {
752 mod = (double)(data_current->rx + data_current->tx) / (double)(data_previous->rx + data_previous->tx);
753 rxp = rxp * mod;
754 txp = txp * mod;
755 }
756
757 /* move graph to center if there's only one to draw for this line */
758 if (data_previous == NULL) {
759 offset = 85 + (ic->large * 25);
760 }
761
762 textx = x + offset;
763 texty = y;
764
765 drawdonut(ic, textx + 50 + (ic->large * 40), texty + 45 + (ic->large * 10), (float)rxp, (float)txp, 49 + (ic->large * 10), 15 + (ic->large * 3));
766
767 if (mode[0] == 'd') {
768 /* get formatted date for today */
769 d = localtime(&ic->current);
770 strftime(datebuff, 16, cfg.dformat, d);
771
772 /* get formatted date for current day in database */
773 d = localtime(&data_current->timestamp);
774 strftime(daytemp, 16, cfg.dformat, d);
775
776 /* change daytemp to today if formatted days match */
777 if (strcmp(datebuff, daytemp) == 0) {
778 strncpy_nt(daytemp, "today", 32);
779 }
780 } else if (mode[0] == 'm') {
781 d = localtime(&data_current->timestamp);
782 strftime(daytemp, 16, cfg.mformat, d);
783 }
784
785 snprintf(buffer, 32, "%*s", getpadding(12, daytemp), daytemp);
786 gdImageString(ic->im, titlefont, textx - 54 + (ic->large * (ic->font->w * 3 - 4)), texty - 1, (unsigned char *)buffer, ic->ctext);
787
788 if (cfg.summaryrate) {
789 d = localtime(&ic->interface.updated);
790 if (mode[0] == 'd') {
791 snprintf(buffer, 16, "%15s", gettrafficrate(data_current->rx + data_current->tx, d->tm_sec + (d->tm_min * 60) + (d->tm_hour * 3600), 15));
792 } else if (mode[0] == 'm') {
793 snprintf(buffer, 16, "%15s", gettrafficrate(data_current->rx + data_current->tx, mosecs(data_current->timestamp, ic->interface.updated), 15));
794 }
795 gdImageString(ic->im, ic->font, textx - 74, texty + 4 * ic->lineheight + 10, (unsigned char *)buffer, ic->ctext);
796 } else {
797 texty += 7;
798 }
799
800 snprintf(buffer, 4, "rx ");
801 strncat(buffer, getvalue(data_current->rx, 12, RT_Normal), 32);
802 gdImageString(ic->im, ic->font, textx - 74, texty + ic->lineheight + 6, (unsigned char *)buffer, ic->ctext);
803 snprintf(buffer, 4, "tx ");
804 strncat(buffer, getvalue(data_current->tx, 12, RT_Normal), 32);
805 gdImageString(ic->im, ic->font, textx - 74, texty + 2 * ic->lineheight + 6, (unsigned char *)buffer, ic->ctext);
806 snprintf(buffer, 4, " = ");
807 strncat(buffer, getvalue(data_current->rx + data_current->tx, 12, RT_Normal), 32);
808 gdImageString(ic->im, ic->font, textx - 74, texty + 3 * ic->lineheight + 8, (unsigned char *)buffer, ic->ctext);
809
810 /* previous entry */
811 if (data_previous != NULL) {
812 if (data_previous->rx + data_previous->tx == 0) {
813 rxp = txp = 0;
814 } else {
815 rxp = (double)data_previous->rx / (double)(data_previous->rx + data_previous->tx) * 100;
816 txp = (double)100 - rxp;
817 }
818
819 /* do scaling if needed */
820 if ((data_previous->rx + data_previous->tx) < (data_current->rx + data_current->tx)) {
821 mod = (double)(data_previous->rx + data_previous->tx) / (double)(data_current->rx + data_current->tx);
822 rxp = rxp * mod;
823 txp = txp * mod;
824 }
825
826 textx += 180 + (ic->large * 60);
827
828 drawdonut(ic, textx + 50 + (ic->large * 40), texty + 45 + (ic->large * 10), (float)rxp, (float)txp, 49 + (ic->large * 10), 15 + (ic->large * 3));
829
830 if (mode[0] == 'd') {
831 /* get formatted date for yesterday */
832 d = localtime(&yesterday);
833 strftime(datebuff, 16, cfg.dformat, d);
834
835 /* get formatted date for previous day in database */
836 d = localtime(&data_previous->timestamp);
837 strftime(daytemp, 16, cfg.dformat, d);
838
839 /* change daytemp to yesterday if formatted days match */
840 if (strcmp(datebuff, daytemp) == 0) {
841 strncpy_nt(daytemp, "yesterday", 32);
842 }
843 } else if (mode[0] == 'm') {
844 d = localtime(&data_previous->timestamp);
845 strftime(daytemp, 16, cfg.mformat, d);
846 }
847
848 snprintf(buffer, 32, "%*s", getpadding(12, daytemp), daytemp);
849 gdImageString(ic->im, titlefont, textx - 54 + (ic->large * (ic->font->w * 3 - 4)), texty - 1, (unsigned char *)buffer, ic->ctext);
850
851 if (cfg.summaryrate) {
852 if (mode[0] == 'd') {
853 snprintf(buffer, 16, "%15s", gettrafficrate(data_previous->rx + data_previous->tx, 86400, 15));
854 } else if (mode[0] == 'm') {
855 snprintf(buffer, 16, "%15s", gettrafficrate(data_previous->rx + data_previous->tx, dmonth(d->tm_mon) * 86400, 15));
856 }
857 gdImageString(ic->im, ic->font, textx - 74, texty + 4 * ic->lineheight + 10, (unsigned char *)buffer, ic->ctext);
858 } else {
859 texty += 7;
860 }
861
862 snprintf(buffer, 4, "rx ");
863 strncat(buffer, getvalue(data_previous->rx, 12, RT_Normal), 32);
864 gdImageString(ic->im, ic->font, textx - 74, texty + ic->lineheight + 6, (unsigned char *)buffer, ic->ctext);
865 snprintf(buffer, 4, "tx ");
866 strncat(buffer, getvalue(data_previous->tx, 12, RT_Normal), 32);
867 gdImageString(ic->im, ic->font, textx - 74, texty + 2 * ic->lineheight + 6, (unsigned char *)buffer, ic->ctext);
868 snprintf(buffer, 4, " = ");
869 strncat(buffer, getvalue(data_previous->rx + data_previous->tx, 12, RT_Normal), 32);
870 gdImageString(ic->im, ic->font, textx - 74, texty + 3 * ic->lineheight + 8, (unsigned char *)buffer, ic->ctext);
871 }
872
873 data_current = NULL;
874 data_previous = NULL;
875 dbdatalistfree(&datalist);
876 }
877
drawfivegraph(IMAGECONTENT * ic,const int rate,const int resultcount,const int height)878 void drawfivegraph(IMAGECONTENT *ic, const int rate, const int resultcount, const int height)
879 {
880 int imagewidth, imageheight = height, headermod = 0;
881
882 imagewidth = resultcount + FIVEMINEXTRASPACE + (ic->large * 14);
883
884 if (!ic->showheader) {
885 headermod = 22;
886 }
887
888 imageinit(ic, imagewidth, imageheight);
889 layoutinit(ic, " / 5 minute", imagewidth, imageheight);
890
891 if (drawfiveminutes(ic, 8 + (ic->large * 14), imageheight - 30 - (ic->large * 8), rate, resultcount, imageheight - 68 + headermod - (ic->large * 8))) {
892 drawlegend(ic, imagewidth / 2 - (ic->large * 10), imageheight - 17 - (ic->large * 2), 0);
893 }
894 }
895
drawfiveminutes(IMAGECONTENT * ic,const int xpos,const int ypos,const int rate,const int resultcount,const int height)896 int drawfiveminutes(IMAGECONTENT *ic, const int xpos, const int ypos, const int rate, const int resultcount, const int height)
897 {
898 int x = xpos, y = ypos, i = 0, t = 0, rxh = 0, txh = 0, step = 0, s = 0, prev = 0;
899 uint64_t scaleunit, max;
900 time_t timestamp;
901 double ratediv, e;
902 char buffer[32];
903 struct tm *d;
904 dbdatalist *datalist = NULL, *datalist_i = NULL;
905 dbdatalistinfo datainfo;
906 gdFontPtr font;
907
908 if (ic->large) {
909 font = gdFontGetSmall();
910 } else {
911 font = gdFontGetTiny();
912 }
913
914 if (!db_getdata(&datalist, &datainfo, ic->interface.name, "fiveminute", (uint32_t)resultcount) || datainfo.count == 0) {
915 x = (resultcount + FIVEMINEXTRASPACE + (ic->large * 14)) / 2 - (8 * ic->font->w + ic->font->w / 2);
916 gdImageString(ic->im, ic->font, x, y - (height / 2) - ic->font->h, (unsigned char *)"no data available", ic->ctext);
917 return 0;
918 }
919
920 datalist_i = datalist;
921
922 if (cfg.rateunit) {
923 ratediv = 37.5; /* x * 8 / 300 */
924 } else {
925 ratediv = 300;
926 }
927
928 /* axis */
929 x += 36;
930 gdImageLine(ic->im, x, y, x + (resultcount + FIVEMINWIDTHFULLPADDING), y, ic->ctext);
931 gdImageLine(ic->im, x + 4, y + 4, x + 4, y - height, ic->ctext);
932
933 /* arrows */
934 drawarrowup(ic, x + 4, y - 1 - height);
935 drawarrowright(ic, x + 1 + (resultcount + FIVEMINWIDTHFULLPADDING), y);
936
937 max = datainfo.maxrx + datainfo.maxtx;
938
939 if (datainfo.maxrx == datainfo.maxtx) {
940 txh = (int)((height - FIVEMINHEIGHTOFFSET * 2) / 2);
941 rxh = height - FIVEMINHEIGHTOFFSET * 2 - txh;
942 max = (uint64_t)((double)datainfo.maxrx / ratediv);
943 t = rxh;
944 } else if (datainfo.maxrx > datainfo.maxtx) {
945 txh = (int)lrint(((double)datainfo.maxtx / (double)max) * (height - FIVEMINHEIGHTOFFSET * 2));
946 rxh = height - FIVEMINHEIGHTOFFSET * 2 - txh;
947 max = (uint64_t)((double)datainfo.maxrx / ratediv);
948 t = rxh;
949 } else {
950 rxh = (int)lrint(((double)datainfo.maxrx / (double)max) * (height - FIVEMINHEIGHTOFFSET * 2));
951 txh = height - FIVEMINHEIGHTOFFSET * 2 - rxh;
952 max = (uint64_t)((double)datainfo.maxtx / ratediv);
953 t = txh;
954 }
955
956 /* center line */
957 x += 5;
958 y -= txh + FIVEMINHEIGHTOFFSET;
959 gdImageLine(ic->im, x, y, x + (resultcount + FIVEMINWIDTHPADDING), y, ic->ctext);
960 gdImageString(ic->im, font, x - 21 - (ic->large * 3), y - 4 - (ic->large * 3), (unsigned char *)" 0", ic->ctext);
961
962 /* scale values */
963 scaleunit = getscale(max, rate);
964
965 s = (int)lrint(((double)scaleunit / (double)max) * t);
966 if (s == 0) {
967 s = 1; // force to show something when there's not much or any traffic, scale is likely to be wrong in this case
968 }
969 while (s * step < SCALEMINPIXELS) {
970 step++;
971 }
972
973 if (debug) {
974 printf("maxrx: %" PRIu64 "\n", datainfo.maxrx);
975 printf("maxtx: %" PRIu64 "\n", datainfo.maxtx);
976 printf("rxh: %d txh: %d\n", rxh, txh);
977 printf("max divided: %" PRIu64 "\n", max);
978 printf("scaleunit: %" PRIu64 "\nstep: %d\n", scaleunit, step);
979 printf("pixels per step: %d\n", s);
980 printf("mintime: %" PRIu64 "\nmaxtime: %" PRIu64 "\n", (uint64_t)datainfo.mintime, (uint64_t)datainfo.maxtime);
981 printf("count: %u\n", datainfo.count);
982 }
983
984 /* upper part scale values */
985 y--; // adjust to start above center line
986 for (i = step; i * s <= rxh; i = i + step) {
987 gdImageDashedLine(ic->im, x, y - (i * s), x + (resultcount + FIVEMINWIDTHPADDING), y - (i * s), ic->cline);
988 gdImageDashedLine(ic->im, x, y - prev - (step * s) / 2, x + (resultcount + FIVEMINWIDTHPADDING), y - prev - (step * s) / 2, ic->clinel);
989 gdImageString(ic->im, font, x - 21 - (ic->large * 3), y - 3 - (i * s) - (ic->large * 3), (unsigned char *)getimagevalue(scaleunit * (unsigned int)i, 3, rate), ic->ctext);
990 prev = i * s;
991 }
992 if ((prev + (step * s) / 2) <= rxh) {
993 gdImageDashedLine(ic->im, x, y - prev - (step * s) / 2, x + (resultcount + FIVEMINWIDTHPADDING), y - prev - (step * s) / 2, ic->clinel);
994 }
995
996 y += 2; // adjust to start below center line
997 prev = 0;
998
999 /* lower part scale values */
1000 for (i = step; i * s <= txh; i = i + step) {
1001 gdImageDashedLine(ic->im, x, y + (i * s), x + (resultcount + FIVEMINWIDTHPADDING), y + (i * s), ic->cline);
1002 gdImageDashedLine(ic->im, x, y + prev + (step * s) / 2, x + (resultcount + FIVEMINWIDTHPADDING), y + prev + (step * s) / 2, ic->clinel);
1003 gdImageString(ic->im, font, x - 21 - (ic->large * 3), y - 3 + (i * s) - (ic->large * 3), (unsigned char *)getimagevalue(scaleunit * (unsigned int)i, 3, rate), ic->ctext);
1004 prev = i * s;
1005 }
1006 if ((prev + (step * s) / 2) <= txh) {
1007 gdImageDashedLine(ic->im, x, y + prev + (step * s) / 2, x + (resultcount + FIVEMINWIDTHPADDING), y + prev + (step * s) / 2, ic->clinel);
1008 }
1009
1010 y--; // y is now back on center line
1011
1012 /* scale text */
1013 gdImageStringUp(ic->im, font, x - 39 - (ic->large * 14), ypos - height / 2 + (rate * 10), (unsigned char *)getimagescale(scaleunit * (unsigned int)step, rate), ic->ctext);
1014
1015 timestamp = datainfo.maxtime - (resultcount * 300);
1016
1017 while (datalist_i != NULL && datalist_i->timestamp < timestamp + 300) {
1018 if (debug) {
1019 printf("Skip data, %" PRIu64 " < %" PRIu64 "\n", (uint64_t)datalist_i->timestamp, (uint64_t)timestamp + 300);
1020 }
1021 datalist_i = datalist_i->next;
1022 }
1023
1024 for (i = 0; i < resultcount; i++) {
1025
1026 if (datalist_i == NULL) {
1027 break;
1028 }
1029
1030 timestamp += 300;
1031 d = localtime(×tamp);
1032
1033 if (d->tm_min == 0 && i > 2) {
1034 if (d->tm_hour % 2 == 0) {
1035 if (d->tm_hour == 0) {
1036 gdImageLine(ic->im, x + i, y + txh - 1 + FIVEMINHEIGHTOFFSET, x + i, y - rxh - 1, ic->cline);
1037 } else {
1038 gdImageLine(ic->im, x + i, y + txh - 1 + FIVEMINHEIGHTOFFSET, x + i, y - rxh - 1, ic->cbgoffset);
1039 }
1040
1041 if (i > font->w) {
1042 snprintf(buffer, 32, "%02d", d->tm_hour);
1043 if (datalist_i->timestamp > timestamp) {
1044 gdImageString(ic->im, font, x + i - font->w + 1, y + txh + font->h - (ic->large * 5), (unsigned char *)buffer, ic->cline);
1045 } else {
1046 gdImageString(ic->im, font, x + i - font->w + 1, y + txh + font->h - (ic->large * 5), (unsigned char *)buffer, ic->ctext);
1047 }
1048 }
1049 } else {
1050 gdImageLine(ic->im, x + i, y + txh - 1 + FIVEMINHEIGHTOFFSET, x + i, y - rxh - 1, ic->cbgoffset);
1051 }
1052 gdImageSetPixel(ic->im, x + i, y, ic->ctext);
1053 }
1054
1055 if (datalist_i->timestamp > timestamp) {
1056 gdImageSetPixel(ic->im, x + i, y, ic->cline);
1057 gdImageSetPixel(ic->im, x + i, y + txh + FIVEMINHEIGHTOFFSET, ic->cline);
1058 continue;
1059 }
1060
1061 /* only the last entry can be the currently ongoing period that may need scaling */
1062 if (datalist_i->next == NULL && issametimeslot(LT_5min, datalist_i->timestamp, ic->interface.updated)) {
1063 e = (double)(ic->interface.updated - datalist_i->timestamp) / (double)300;
1064 if (e < 0.01) {
1065 e = 1;
1066 }
1067 } else {
1068 e = 1;
1069 }
1070
1071 t = (int)lrint(((double)datalist_i->rx / e / (double)datainfo.maxrx) * rxh);
1072 if (t > rxh) {
1073 t = rxh;
1074 }
1075 drawpole(ic, x + i, y - 1, t, 1, ic->crx);
1076
1077 t = (int)lrint(((double)datalist_i->tx / e / (double)datainfo.maxtx) * txh);
1078 if (t > txh) {
1079 t = txh;
1080 }
1081 drawpole(ic, x + i, y + 1, t, 2, ic->ctx);
1082
1083 datalist_i = datalist_i->next;
1084 }
1085
1086 dbdatalistfree(&datalist);
1087
1088 return 1;
1089 }
1090