/*
 *  Copyright (C) 1998-2012 Luca Deri <deri@ntop.org>
 *
 *  			    http://www.ntop.org/
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "ntop.h"
#include "globals-report.h"

typedef struct osInfo {
  char *name, *link;
} OsInfo;

typedef struct osNumInfo {
  char *name;
  unsigned short num;
} OsNumInfo;

static OsInfo osInfos[] = {
  { "Windows",       CONST_IMG_OS_WINDOWS },
  { "IRIX",          CONST_IMG_OS_IRIX },
  { "Linux",         CONST_IMG_OS_LINUX },
  { "SunOS",         CONST_IMG_OS_SUNOS },
  { "Solaris",       CONST_IMG_OS_SOLARIS },
  { "HP/JETdirect",  CONST_IMG_OS_HP_JETDIRET },
  { "Mac",           CONST_IMG_OS_MAC },
  { "Novell",        CONST_IMG_OS_NOVELL },
  { "BSD",           CONST_IMG_OS_BSD },
  { "Unix",          CONST_IMG_OS_UNIX },
  { "Berkeley",      CONST_IMG_OS_BERKELEY },
  { "HP-UX",         CONST_IMG_OS_HP_UX },
  { "AIX",           CONST_IMG_OS_AIX },
  { "Cisco",         CONST_IMG_OS_CISCO },
  { NULL, NULL }
};

/* ************************* */

void *mallocAndInitWithReportWarn(int sz, char *from) {
  void *tmpTable;

  tmpTable = malloc(sz);

  if (tmpTable == NULL) {
    traceEvent(CONST_TRACE_ERROR, "Unable to allocate temporary table (%d) for %s", sz, from);
    traceEvent(CONST_TRACE_INFO,  "User warned, continuing without generating report");
    printFlagedWarning("SORRY: <i>An internal error does not allow creation of this report.</i>");
  } else {
    memset (tmpTable, 0, sz);
  }

  return tmpTable;
}

/* ************************************ */

void formatUsageCounter(UsageCounter usageCtr,
			Counter topValue,
			/* If this value != 0 then a percentage is printed */
			int actualDeviceId) {
  char buf[LEN_GENERAL_WORK_BUFFER], formatBuf[32], hostLinkBuf[3*LEN_GENERAL_WORK_BUFFER];
  int i, sendHeader=0;
  HostTraffic *el;

  if(topValue == 0) {
    /* No percentage is printed */
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
		formatPkts(usageCtr.value.value, formatBuf, sizeof(formatBuf)));
    sendString(buf);
  } else {
    float pctg;

    pctg = ((float)usageCtr.value.value/(float)topValue)*100;

    if(pctg > 100) pctg = 100; /* This should not happen ! */

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TD "TD_BG" ALIGN=RIGHT>%s&nbsp;[%.0f&nbsp;%%]</TD>",
		formatPkts(usageCtr.value.value, formatBuf, sizeof(formatBuf)), pctg);
    sendString(buf);
  }

  for(i=0; i<MAX_NUM_CONTACTED_PEERS; i++) {
      HostTraffic tmpEl;

      if(!emptySerial(&usageCtr.peersSerials[i])) {
	  if((el = quickHostLink(usageCtr.peersSerials[i],
				 myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
 	if(!sendHeader) {
	  sendString("<TD "TD_BG" ALIGN=LEFT><ul>");
	  sendHeader = 1;
	}

	sendString("\n<li>");
	sendString(makeHostLink(el, 0, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)));
      } else
	  traceEvent(CONST_TRACE_WARNING, "Unable to find host serial - host skipped");
    }
  }

  if(sendHeader)
    sendString("</ul></TD>\n");
  else
    sendString("<TD "TD_BG">&nbsp;</TD>\n");
}

/* ********************************** */

void printTableDoubleEntry(char *buf, int bufLen,
			   char *label, char* color,
			   float totalS, float percentageS,
			   float totalR, float percentageR) {
  int int_perc;
  char formatBuf[32];

  if((totalS == 0) && (totalR == 0)) return;

  if(percentageS < 0.5) {
    int_perc = 0;
    percentageS = 0;
  } else if(percentageS > 99.5) {
    int_perc = 100;
    percentageS = 100;
  } else {
    int_perc = (int)(percentageS + 0.5);
  }

  switch(int_perc) {
  case 0:
    safe_snprintf(__FILE__, __LINE__, buf, bufLen, "<TR "TR_ON" %s><TH WIDTH=100 "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
           "<TD WIDTH=100 "TD_BG" ALIGN=RIGHT>%s</TD>"
           "<TD WIDTH=100 "TD_BG">&nbsp;</TD>\n",
           getRowColor(), label, formatKBytes(totalS, formatBuf, sizeof(formatBuf)));
    break;
  case 100:
    safe_snprintf(__FILE__, __LINE__, buf, bufLen, "<TR "TR_ON" %s><TH WIDTH=100 "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		"<TD WIDTH=100 "TD_BG" ALIGN=RIGHT>%s</TD>"
		"<TD WIDTH=100><IMG ALT=\"100%%\" ALIGN=MIDDLE SRC=\"/gauge.jpg\" WIDTH=100 HEIGHT=12></TD>\n",
		getRowColor(), label, formatKBytes(totalS, formatBuf, sizeof(formatBuf)));
    break;
  default:
    safe_snprintf(__FILE__, __LINE__, buf, bufLen, "<TR "TR_ON" %s><TH WIDTH=100 "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		"<TD WIDTH=100 "TD_BG" ALIGN=RIGHT>%s</TD>"
		"<TD WIDTH=100 "TD_BG"><TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=\"100\">"
		"<TR "TR_ON"><TD><IMG  ALT=\"%d%%\" ALIGN=MIDDLE SRC=\"/gauge.jpg\" WIDTH=\"%d\" HEIGHT=12></TD>"
		"<TD "TD_BG" ALIGN=CENTER WIDTH=\"%d\">"
		"<P>&nbsp;</TD></TR>\n</TABLE>"TABLE_OFF"</TD>\n",
		getRowColor(), label, formatKBytes(totalS, formatBuf, sizeof(formatBuf)),
		int_perc, (100*int_perc)/100, (100*(100-int_perc))/100);
  }

  sendString(buf);

  /* ************************ */

  if(totalR == 0) percentageR = 0;

  if(percentageR < 0.5) {
    int_perc = 0;
    percentageR = 0;
  } else if(percentageR > 99.5) {
    int_perc = 100;
    percentageR = 100;
  } else {
    int_perc = (int)(percentageR + 0.5);
  }

  switch(int_perc) {
  case 0:
    safe_snprintf(__FILE__, __LINE__, buf, bufLen, "<TD WIDTH=100 "TD_BG" ALIGN=RIGHT>%s</TD>"
		"<TD WIDTH=100 "TD_BG">&nbsp;</TD></TR>\n\n",
		formatKBytes(totalR, formatBuf, sizeof(formatBuf)));
    break;
  case 100:
    safe_snprintf(__FILE__, __LINE__, buf, bufLen, "<TD WIDTH=100 "TD_BG" ALIGN=RIGHT>%s</TD>"
		"<TD WIDTH=100><IMG ALIGN=MIDDLE ALT=\"100\" SRC=\"/gauge.jpg\" WIDTH=\"100\" HEIGHT=12></TD></TR>\n\n",
		formatKBytes(totalR, formatBuf, sizeof(formatBuf)));
    break;
  default:
    safe_snprintf(__FILE__, __LINE__, buf, bufLen, "<TD WIDTH=100 "TD_BG" ALIGN=RIGHT>%s</TD>"
		"<TD  WIDTH=100 "TD_BG"><TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=\"100\">"
		"<TR "TR_ON"><TD><IMG ALT=\"%d%%\" ALIGN=MIDDLE SRC=\"/gauge.jpg\" WIDTH=\"%d\" HEIGHT=12>"
		"</TD><TD "TD_BG" ALIGN=CENTER WIDTH=\"%d\">"
		"<P>&nbsp;</TD></TR>\n</TABLE></TD></TR>\n\n",
		formatKBytes(totalR, formatBuf, sizeof(formatBuf)),
		int_perc, (100*int_perc)/100, (100*(100-int_perc))/100);
  }

  sendString(buf);
}

/* ********************************** */

void printTableEntryPercentage(char *buf, int bufLen,
			       char *label, char* label_1,
			       char* label_2, float total,
			       float percentage,
			       u_int showFlows, Counter flows) {
  int int_perc;
  char formatBuf[32], flowBuf[64], tmpBuf[32];

  if(percentage < 0.5)
    int_perc = 0;
  else if(percentage > 99.5)
    int_perc = 100;
  else
    int_perc = (int) (percentage + 0.5);

  if(showFlows == 0)
    flowBuf[0] = '\0';
  else
    safe_snprintf(__FILE__, __LINE__, flowBuf, sizeof(flowBuf),
		  "</TD><TD "TD_BG" ALIGN=RIGHT WIDTH=50>%s",
		  formatPkts(flows, tmpBuf, sizeof(tmpBuf)));

  switch(int_perc) {
  case 0:
    if(total == -1) {
      safe_snprintf(__FILE__, __LINE__, buf, bufLen,
		    "\n<TR %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		    "<TD "TD_BG">\n<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=\"100%%\">"
		    "\n<TR>"
		    "<TD ALIGN=LEFT WIDTH=\"10%%\" BGCOLOR=\"%s\">%s 0&nbsp;%%</TD>"
		    "<TD><TABLE BORDER=1 CELLPADDING=1 CELLSPACING=0 WIDTH=\"100%%\">\n<TR>"
		    "<TD ALIGN=CENTER WIDTH=\"100%%\" BGCOLOR=\"%s\">&nbsp;</TD>"
		    "</TR>\n</TABLE></TD>"
		    "<TD ALIGN=RIGHT WIDTH=\"10%%\" BGCOLOR=\"%s\">%s 100&nbsp;%%</TD></TR>\n</TABLE></TD></TR>\n\n",
		    getRowColor(), label, CONST_COLOR_1, flowBuf, /* label_1, */
		    CONST_COLOR_2, CONST_COLOR_2, label_2);
    } else {
      safe_snprintf(__FILE__, __LINE__, buf, bufLen,
		    "\n<TR %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		    "<TD "TD_BG" ALIGN=RIGHT>%s %s</TD>"
		    "<TD "TD_BG">\n<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=\"100%%\">"
		    "\n<TR>"
		    "<TD ALIGN=LEFT WIDTH=\"10%%\"  BGCOLOR=\"%s\">%s 0&nbsp;%%</TD>"
		    "<TD><TABLE BORDER=1 CELLPADDING=1 CELLSPACING=0 WIDTH=\"100%%\">\n<TR>"
		    "<TD ALIGN=CENTER WIDTH=\"100%%\" BGCOLOR=\"%s\">&nbsp;</TD>"
		    "</TR>\n</TABLE></TD>"
		    "<TD ALIGN=RIGHT WIDTH=\"10%%\" BGCOLOR=\"%s\">%s 100&nbsp;%%</TD></TR>\n</TABLE></TD></TR>\n\n",
		    getRowColor(), label,
		    formatKBytes(total, formatBuf, sizeof(formatBuf)), flowBuf,
		    CONST_COLOR_1, label_1, CONST_COLOR_2,
		    CONST_COLOR_1, label_2);
    }
    break;
  case 100:
    if(total == -1) {
      safe_snprintf(__FILE__, __LINE__, buf, bufLen, "\n<TR %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		    "<TD "TD_BG">"
		    "\n<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=\"100%%\">"
		    "\n<TR>"
		    "<TD ALIGN=LEFT WIDTH=\"10%%\" BGCOLOR=\"%s\">%s 100&nbsp;%%</TD>"
		    "<TD><TABLE BORDER=1 CELLPADDING=1 CELLSPACING=0 WIDTH=\"100%%\">\n<TR>"
		    "<TD ALIGN=CENTER WIDTH=\"100%%\" BGCOLOR=\"%s\">&nbsp;</TD>"
		    "</TR>\n</TABLE></TD>"
		    "<TD ALIGN=RIGHT WIDTH=\"10%%\" BGCOLOR=\"%s\">%s 0&nbsp;%%</TD></TR>\n</TABLE></TD></TR>\n\n",
		    getRowColor(), label,
		    CONST_COLOR_1, label_1,
		    CONST_COLOR_1,
		    CONST_COLOR_2, label_2);
    } else {
      safe_snprintf(__FILE__, __LINE__, buf, bufLen,
		    "\n<TR %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		    "<TD "TD_BG" ALIGN=RIGHT>%s %s</TD>"
		    "<TD "TD_BG">\n<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=\"100%%\">"
		    "\n<TR>"
		    "<TD ALIGN=LEFT WIDTH=\"10%%\" BGCOLOR=\"%s\">%s 100&nbsp;%%</TD>"
		    "<TD><TABLE BORDER=1 CELLPADDING=1 CELLSPACING=0 WIDTH=\"100%%\">\n<TR>"
		    "<TD ALIGN=CENTER WIDTH=\"100%%\" BGCOLOR=\"%s\">&nbsp;</TD>"
		    "</TR>\n</TABLE></TD>"
		    "<TD ALIGN=RIGHT WIDTH=\"10%%\" BGCOLOR=\"%s\">%s 0&nbsp;%%</TD></TR>\n</TABLE></TD></TR>\n\n",
		    getRowColor(), label,
		    formatKBytes(total, formatBuf, sizeof(formatBuf)), flowBuf,
		    CONST_COLOR_1, label_1,
		    CONST_COLOR_1,
		    CONST_COLOR_2, label_2);
    }
    break;
  default:
    if(total == -1) {
      safe_snprintf(__FILE__, __LINE__, buf, bufLen,
		    "\n<TR %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		    "<TD "TD_BG">\n"
		    "<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=\"100%%\">"
		    "\n<TR>"
		    "<TD ALIGN=LEFT WIDTH=\"10%%\" BGCOLOR=\"%s\">%s %.1f&nbsp;%%</TD>"
		    "<TD><TABLE BORDER=1 CELLPADDING=1 CELLSPACING=0 WIDTH=\"100%%\">\n<TR>"
		    "<TD ALIGN=CENTER WIDTH=\"%d%%\" BGCOLOR=\"%s\">&nbsp;</TD>"
		    "<TD ALIGN=CENTER WIDTH=\"%d%%\" BGCOLOR=\"%s\">&nbsp;</TD>"
		    "</TR>\n</TABLE></TD>"
		    "<TD ALIGN=RIGHT WIDTH=\"10%%\" BGCOLOR=\"%s\">%s %.1f&nbsp;%%</TD></TR>\n</TABLE></TD></TR>\n\n",
		    getRowColor(), label,
		    CONST_COLOR_1, label_1, percentage,
		    int_perc, CONST_COLOR_1,
		    (100-int_perc), CONST_COLOR_2,
		    CONST_COLOR_2, label_2, (100-percentage));
    } else {
      safe_snprintf(__FILE__, __LINE__, buf, bufLen,
		    "\n<TR %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>%s %s</TD>"
		    "<TD "TD_BG">\n"
		    "<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0 WIDTH=\"100%%\">"
		    "\n<TR><TD ALIGN=LEFT WIDTH=\"10%%\" BGCOLOR=\"%s\">%s %.1f&nbsp;%%</TD>"
		    "<TD><TABLE BORDER=1 CELLPADDING=1 CELLSPACING=0 WIDTH=\"100%%\">\n<TR>"
		    "<TD ALIGN=CENTER WIDTH=\"%d%%\" BGCOLOR=\"%s\">&nbsp;</TD>"
		    "<TD ALIGN=CENTER WIDTH=\"%d%%\" BGCOLOR=\"%s\">&nbsp;</TD>"
		    "</TR>\n</TABLE></TD>"
		    "<TD ALIGN=RIGHT WIDTH=\"10%%\" BGCOLOR=\"%s\">%s %.1f&nbsp;%%</TD></TR>\n</TABLE></TD></TR>\n\n",
		    getRowColor(), label, formatKBytes(total, formatBuf, sizeof(formatBuf)), flowBuf,
		    CONST_COLOR_1, label_1, percentage,
		    int_perc, CONST_COLOR_1,
		    (100-int_perc), CONST_COLOR_2,
		    CONST_COLOR_2, label_2, (100-percentage));
    }
  }

  sendString(buf);
}

/* ******************************* */

void printFooterHostLink(void) {
  ;
}

/* ******************************* */

static void printFooterTrafficPct(void) {
    char buf[LEN_GENERAL_WORK_BUFFER];

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                 "<P><TABLE BORDER=0 "TABLE_DEFAULTS">"
                   "\n<TR>"
                     "<TD COLSPAN=4>The percentage value is - for a given host - the traffic for that host "
                     "during that hour divided by the total traffic for that host for the last 24 hours.</TD>"
                   "</TR>\n"
                   "\n<TR>"
                     "<TD ALIGN=CENTER NOWRAP "TD_BG" WIDTH=20%%> 0%% </TD>"
                     "<TD ALIGN=CENTER NOWRAP "CONST_CONST_PCTG_LOW_COLOR" WIDTH=20%%>  0%% to %d%% </TD>"
                     "<TD ALIGN=CENTER NOWRAP "CONST_CONST_PCTG_MID_COLOR" WIDTH=20%%> %d%% to %d%% </TD>"
                     "<TD ALIGN=CENTER NOWRAP "CONST_PCTG_HIGH_COLOR" WIDTH=20%%> &gt;%d%% to 100%% </TD>"
                   "</TR>\n"
                 "</TABLE>\n",
                 CONST_PCTG_LOW, CONST_PCTG_LOW, CONST_PCTG_MID, CONST_PCTG_MID);
    sendString(buf);
}

/* ******************************* */

void printFooter(int reportType) {
  switch(reportType) {
    case TRAFFIC_STATS:
        break;
    case SORT_DATA_RECEIVED_PROTOS:
    case SORT_DATA_SENT_PROTOS:
    case SORT_DATA_PROTOS:
    case SORT_DATA_RECEIVED_IP:
    case SORT_DATA_SENT_IP:
    case SORT_DATA_IP:
    case SORT_DATA_RCVD_HOST_TRAFFIC:
    case SORT_DATA_SENT_HOST_TRAFFIC:
    case SORT_DATA_RECEIVED_THPT:
    case SORT_DATA_SENT_THPT:
    case SORT_DATA_THPT:
    case SORT_DATA_HOST_TRAFFIC:
      break;
  }

  sendString("<CENTER>\n");

  switch(reportType) {
    case TRAFFIC_STATS:
        break;
    case SORT_DATA_RECEIVED_PROTOS:
    case SORT_DATA_RECEIVED_IP:
    case SORT_DATA_SENT_PROTOS:
    case SORT_DATA_SENT_IP:
    case SORT_DATA_PROTOS:
    case SORT_DATA_IP:
        printFooterHostLink();
        break;

    case SORT_DATA_RCVD_HOST_TRAFFIC:
    case SORT_DATA_SENT_HOST_TRAFFIC:
    case SORT_DATA_HOST_TRAFFIC:
        printFooterHostLink();
        printFooterTrafficPct();
        break;

    case SORT_DATA_RECEIVED_THPT:
    case SORT_DATA_SENT_THPT:
    case SORT_DATA_THPT:
        printFooterHostLink();
        sendString("<i><P>Peak values are the maximum value for any 10 second interval."
		   "<br>Average values are recomputed each 60 seconds, using values "
		   "accumulated since this run of ntop was started.</P>\n");
        sendString("<P>Note: Both values are reset each time ntop is restarted.</P></i>\n");
        break;
  }

  sendString("</CENTER>\n");
}

/* ******************************* */

void printHeader(int reportType, int revertOrder, u_int column,
		 HostsDisplayPolicy showHostsMode,
		 LocalityDisplayPolicy showLocalityMode,
		 char *vlanList, u_short vlanId) {
  char buf[LEN_GENERAL_WORK_BUFFER];
  char *sign, *arrowGif, *arrow[128], *theAnchor[128], *url=NULL;
  int i, idx, j, hourId, useVlans = 0;
  char htmlAnchor[128], htmlAnchor1[128], theLink[128], theVlanLink[128];
  ProtocolsList *protoList;
  char theDate[8];
  struct tm t;
  char hours[][24] = {"12<BR>AM", "1<BR>AM", "2<BR>AM", "3<BR>AM", "4<BR>AM", "5<BR>AM", "6<BR>AM",
                      "7<BR>AM", "8<BR>AM", "9<BR>AM", "10<BR>AM", "11<BR>AM", "12<BR>PM", "1<BR>PM",
                      "2<BR>PM", "3<BR>PM", "4<BR>PM", "5<BR>PM", "6<BR>PM", "7<BR>PM", "8<BR>PM",
                      "9<BR>PM", "10<BR>PM", "11<BR>PM"};

  for(i=0; i<MAX_VLAN; i++)
    if(vlanList[i] == 1) {
      useVlans = 1;
      break;
    }

  /* printf("->%d<-\n",showHostsMode); */

  strftime(theDate, 8, CONST_TOD_HOUR_TIMESPEC, localtime_r(&myGlobals.actTime, &t));
  hourId = atoi(theDate);

  memset(arrow, 0, sizeof(arrow));
  memset(theAnchor, 0, sizeof(theAnchor));
  memset(htmlAnchor, 0, sizeof(htmlAnchor));
  memset(htmlAnchor1, 0, sizeof(htmlAnchor1));

  if(revertOrder) {
    sign = "";
    arrowGif = "&nbsp;" CONST_IMG_ARROW_UP;
  } else {
    sign = "-";
    arrowGif = "&nbsp;" CONST_IMG_ARROW_DOWN;
  }

  memset(buf, 0, sizeof(buf));

  switch(reportType) {
  case TRAFFIC_STATS:               url = CONST_TRAFFIC_STATS_HTML;               break;
  case SORT_DATA_PROTOS:            url = CONST_SORT_DATA_PROTOS_HTML;            break;
  case SORT_DATA_IP:                url = CONST_SORT_DATA_IP_HTML;                break;
  case SORT_DATA_THPT:              url = CONST_SORT_DATA_THPT_HTML;              break;
  case SORT_DATA_HOST_TRAFFIC:      url = CONST_SORT_DATA_HOST_TRAFFIC_HTML;      break;
  }

  safe_snprintf(__FILE__, __LINE__, htmlAnchor, sizeof(htmlAnchor),
              "<A HREF=\"/%s?showH=%d&amp;showL=%d&amp;col=%s",
              url, showHostsMode, showLocalityMode, sign);
  safe_snprintf(__FILE__, __LINE__, htmlAnchor1, sizeof(htmlAnchor1),
              "<A HREF=\"/%s?showH=%d&amp;showL=%d&amp;col=",
              url, showHostsMode, showLocalityMode);

  if(abs(column) == FLAG_HOST_DUMMY_IDX)
    arrow[0] = arrowGif, theAnchor[0] = htmlAnchor;
    else
    arrow[0] = "", theAnchor[0] = htmlAnchor1;

  if(abs(column) == FLAG_DOMAIN_DUMMY_IDX)
    arrow[1] = arrowGif, theAnchor[1] = htmlAnchor;
  else
    arrow[1] = "",  theAnchor[1] = htmlAnchor1;

  if(abs(column) == 0)
    arrow[2] = arrowGif, theAnchor[2] = htmlAnchor;
  else
    arrow[2] = "", theAnchor[2] = htmlAnchor1;

  if((vlanId > 0) && (vlanId < MAX_VLAN))
    safe_snprintf(__FILE__, __LINE__, theLink, sizeof(theLink), "/%s?col=%s%d&amp;vlan=%d&amp;showL=%d&amp;showH=", url,
		  revertOrder ? "-" : "", column, vlanId, showLocalityMode);
  else
    safe_snprintf(__FILE__, __LINE__, theLink, sizeof(theLink), "/%s?col=%s%d&amp;showL=%d&amp;showH=", url,
		  revertOrder ? "-" : "", column, showLocalityMode);

  safe_snprintf(__FILE__, __LINE__, theVlanLink, sizeof(theVlanLink), "/%s?col=%s%d&amp;showL=%d&amp;showH=%d", url,
	   revertOrder ? "-" : "", column, showLocalityMode, showHostsMode);

  sendString("<CENTER><TABLE WIDTH=100%% BORDER=0 "TABLE_DEFAULTS">\n<TR><TD ALIGN=LEFT>");

  sendString("<p><form action=\"../\">\n<b>Hosts</b>:"
	     "<select onchange=\"window.open(this.options[this.selectedIndex].value,'_top')\">\n");

  switch(showHostsMode) {
  case showOnlyLocalHosts:
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<option value=\"%s0\" >All</option>\n"
		  "<option value=\"%s1\" selected>Local Only</option>\n"
		  "<option value=\"%s2\" >Remote Only</option>\n"
		  "</select>\n",
		  theLink, theLink, theLink);
    break;
  case showOnlyRemoteHosts:
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<option value=\"%s0\" >All</option>\n"
		  "<option value=\"%s1\" >Local Only</option>\n"
		  "<option value=\"%s2\" selected>Remote Only</option>\n"
		  "</select>\n",
		  theLink, theLink, theLink);
    break;
  default:
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<option value=\"%s0\" selected>All</option>\n"
		  "<option value=\"%s1\" >Local Only</option>\n"
		  "<option value=\"%s2\" >Remote Only</option>\n"
		  "</select>\n",
		  theLink, theLink, theLink);
    break;
  }

  sendString(buf);

  if(useVlans) {
    u_char found = 0;

    sendString("<p><b>VLAN</b>: ");

    for(i=0; i<MAX_VLAN; i++)
      if(vlanList[i] == 1) {
	if(i == vlanId)
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "[ <b>%d</b> ] ", i), found = 1;
	else
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "[ <A HREF=\"%s&vlan=%d\">%d</A> ] ", theVlanLink, i, i);

	sendString(buf);
      }

    if(!found)
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "[ <b>All</b> ] ");
    else
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "[ <A HREF=\"%s\">All</A> ] ", theVlanLink);

    sendString(buf);
  }

  sendString("</TD>");

  if(reportType != TRAFFIC_STATS) {
    sendString("<TD ALIGN=right><form action=\"../\">\n<b>Data</b>:"
	       "<select onchange=\"window.open(this.options[this.selectedIndex].value,'_top')\">\n");

    switch(showLocalityMode) {
    case showSentReceived:
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		    "<option value=\"%s?col=%s%d&showH=%d&showL=0\" selected>All</option>\n"
		    "<option value=\"%s?col=%s%d&showH=%d&showL=1\">Sent Only</option>\n"
		    "<option value=\"%s?col=%s%d&showH=%d&showL=2\">Received Only</option>\n",
		    url, revertOrder ? "-" : "", column, showHostsMode,
		    url, revertOrder ? "-" : "", column, showHostsMode,
		    url, revertOrder ? "-" : "", column, showHostsMode);
      break;
    case showOnlySent:
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		    "<option value=\"%s?col=%s%d&showH=%d&showL=0\">All</option>\n"
		    "<option value=\"%s?col=%s%d&showH=%d&showL=1\" selected>Sent Only</option>\n"
		    "<option value=\"%s?col=%s%d&showH=%d&showL=2\">Received Only</option>\n",
		    url, revertOrder ? "-" : "", column, showHostsMode,
		    url, revertOrder ? "-" : "", column, showHostsMode,
		    url, revertOrder ? "-" : "", column, showHostsMode);
      break;
    default:
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		    "<option value=\"%s?col=%s%d&showH=%d&showL=0\">All</option>\n"
		    "<option value=\"%s?col=%s%d&showH=%d&showL=1\">Sent Only</option>\n"
		    "<option value=\"%s?col=%s%d&showH=%d&showL=2\" selected>Received Only</option>\n",
		    url, revertOrder ? "-" : "", column, showHostsMode,
		    url, revertOrder ? "-" : "", column, showHostsMode,
		    url, revertOrder ? "-" : "", column, showHostsMode);
      break;
    }

    sendString(buf);
    sendString("</TD></TR>\n</TABLE></CENTER><p>");
  }

  switch(reportType) {
  case SORT_DATA_RECEIVED_PROTOS:
  case SORT_DATA_SENT_PROTOS:
  case SORT_DATA_PROTOS:
    sendString("<CENTER>\n");
    safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER,
		  ""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG">%s"FLAG_HOST_DUMMY_IDX_STR"\">Host%s</A></TH>\n"
		  "<TH "TH_BG">%s"FLAG_DOMAIN_DUMMY_IDX_STR"\">Location%s</A></TH>"
		  "<TH "TH_BG" COLSPAN=2>%s0\">Data%s</A></TH>\n",
		  theAnchor[0], arrow[0], theAnchor[1], arrow[1],
		  theAnchor[2], arrow[2]);
    sendString(buf);

    for(i=0; i<=15; i++)
      if(abs(column) == i+1) {
	arrow[i] = arrowGif; theAnchor[i] = htmlAnchor;
      } else {
	arrow[i] = ""; theAnchor[i] = htmlAnchor1;
      }

    safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER, "<TH "TH_BG">%s1\">TCP%s</A></TH>"
		  "<TH "TH_BG">%s2\">UDP%s</A></TH><TH "TH_BG">%s3\">ICMP%s</A></TH>"
		  "<TH "TH_BG">%s4\">ICMPv6%s</A></TH>"
		  "</TH><TH "TH_BG">%s7\">IPsec%s</A></TH>"
		  "<TH "TH_BG">%s8\">(R)ARP%s</A></TH></TH>",
		  theAnchor[0], arrow[0], theAnchor[1], arrow[1],
		  theAnchor[2], arrow[2], theAnchor[3], arrow[3],
		  theAnchor[4], arrow[4],
		  theAnchor[7], arrow[7], theAnchor[8], arrow[8]);
    sendString(buf);

    safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER,
		  "<TH "TH_BG">%s11\">NetBios%s</A></TH>"
		  "<TH "TH_BG">%s13\">GRE%s</A></TH>"
		  "<TH "TH_BG">%s14\">IPv6%s</A></TH>"
		  "<TH "TH_BG">%s15\">STP%s</A></TH>",
		  theAnchor[10], arrow[10],
		  theAnchor[12], arrow[12],
		  theAnchor[13], arrow[13],
		  theAnchor[14], arrow[14]);
    sendString(buf);

    protoList = myGlobals.ipProtosList, idx=0;
    while(protoList != NULL) {

      if(abs(column) == BASE_PROTOS_IDX+idx) {
	arrow[BASE_PROTOS_IDX+idx] = arrowGif;
	theAnchor[BASE_PROTOS_IDX+idx] = htmlAnchor;
      } else {
	arrow[BASE_PROTOS_IDX+idx] = "";
	theAnchor[BASE_PROTOS_IDX+idx] = htmlAnchor1;
      }

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TH "TH_BG">%s%d\">%s%s</A></TH>",
		    theAnchor[BASE_PROTOS_IDX+idx], BASE_PROTOS_IDX+idx,
		    protoList->protocolName, arrow[BASE_PROTOS_IDX+idx]);
      sendString(buf);

      idx++, protoList = protoList->next;
    }

    safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER,
		"<TH "TH_BG">%s16\">Other%s</A></TH>",
		theAnchor[15], arrow[15]);
    sendString(buf);
    break;

  case SORT_DATA_RECEIVED_IP:
  case SORT_DATA_SENT_IP:
  case SORT_DATA_IP:
    sendString("<CENTER>\n");
    safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER, ""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "TR_ON" "DARK_BG">"
		"<TH "TH_BG">%s"FLAG_HOST_DUMMY_IDX_STR"\">Host%s</A></TH>\n"
		"<TH "TH_BG">%s"FLAG_DOMAIN_DUMMY_IDX_STR"\">Location%s</A></TH>"
		"<TH "TH_BG" COLSPAN=2>%s0\">Data%s</A></TH>\n",
		theAnchor[0], arrow[0], theAnchor[1], arrow[1],
		theAnchor[2], arrow[2]);
    sendString(buf);
    if(abs(column) == 1) {
      arrow[0] = arrowGif;
      theAnchor[0] = htmlAnchor;
    } else {
      arrow[0] = "";
      theAnchor[0] = htmlAnchor1;
    }


    for(i=0; i<myGlobals.l7.numSupportedProtocols; i++) {
      if(myGlobals.device[myGlobals.actualReportDeviceId].l7.protoTraffic[i] > 0) {
	if(abs(column) == (i+2)) {
	  arrow[0] = arrowGif;
	  theAnchor[0] = htmlAnchor;
	} else {
	  arrow[0] = "";
	  theAnchor[0] = htmlAnchor1;
	}
	safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER, "<TH "TH_BG">%s%d\">%s%s</A></TH>",
		      theAnchor[0], i+2, getProtoName(0, i), arrow[0]);
	sendString(buf);
      }
    }
    break;

  case SORT_DATA_RCVD_HOST_TRAFFIC:
  case SORT_DATA_SENT_HOST_TRAFFIC:
  case SORT_DATA_HOST_TRAFFIC:
    sendString("<CENTER>\n");
    safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER, ""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "DARK_BG">"
		"<TH "TH_BG">%s"FLAG_HOST_DUMMY_IDX_STR"\">Host%s</A></TH>"
		"<TH "TH_BG">%s"FLAG_DOMAIN_DUMMY_IDX_STR"\">Location%s</A></TH>\n",
		theAnchor[0], arrow[0], theAnchor[1], arrow[1]);
    sendString(buf);
    j = hourId;
    for (i = 0; i < 24; i++) {
        j = j % 24;
        safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TH "TH_BG">%s</TH>\n", hours[j]);
        sendString (buf);
        if (!j) {
            j = 23;
        }
        else {
            j--;
        }
    }
    break;
  case SORT_DATA_RECEIVED_THPT:
  case SORT_DATA_SENT_THPT:
  case SORT_DATA_THPT:
    sendString("<CENTER>\n");
    safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER, ""TABLE_ON
		  "<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG" ROWSPAN=\"2\">%s"FLAG_HOST_DUMMY_IDX_STR"\">Host%s</A></TH>"
		  "<TH "TH_BG" ROWSPAN=\"2\">%s"FLAG_DOMAIN_DUMMY_IDX_STR"\">Location%s</A></TH>\n\n",
		  theAnchor[0], arrow[0], theAnchor[1], arrow[1]);
    sendString(buf);

    updateThpt(0);
    if(abs(column) == 1) { arrow[0] = arrowGif; theAnchor[0] = htmlAnchor; }
    else { arrow[0] = ""; theAnchor[0] = htmlAnchor1;  }
    if(abs(column) == 2) { arrow[1] = arrowGif; theAnchor[1] = htmlAnchor; }
    else { arrow[1] = ""; theAnchor[1] = htmlAnchor1; }
    if(abs(column) == 3) { arrow[2] = arrowGif; theAnchor[2] = htmlAnchor; }
    else { arrow[2] = "";  theAnchor[2] = htmlAnchor1;}
    if(abs(column) == 4) { arrow[3] = arrowGif; theAnchor[3] = htmlAnchor; }
    else { arrow[3] = "";  theAnchor[3] = htmlAnchor1;}
    if(abs(column) == 5) { arrow[4] = arrowGif; theAnchor[4] = htmlAnchor; }
    else { arrow[4] = "";  theAnchor[4] = htmlAnchor1;}
    if(abs(column) == 6) { arrow[5] = arrowGif; theAnchor[5] = htmlAnchor; }
    else { arrow[5] = "";  theAnchor[5] = htmlAnchor1;}

    safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER, "<TH "TH_BG" COLSPAN=\"3\" ALIGN=CENTER>Data</TH>"
		"</TR>\n<TR "TR_ON" "DARK_BG">");
    sendString(buf);
    safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER, "<TH "TH_BG">%s1\">Current%s</A></TH>"
		  "<TH "TH_BG">%s2\">Avg%s</A></TH>"
		  "<TH "TH_BG">%s3\">Peak%s</A></TH>",
		  theAnchor[0], arrow[0], theAnchor[1], arrow[1], theAnchor[2], arrow[2]);
    sendString(buf);
    break;
  case TRAFFIC_STATS:
    sendString("<CENTER>\n");
    safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER,
		  ""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG">%s"FLAG_HOST_DUMMY_IDX_STR"\">Host%s</A></TH>"
		  "<TH "TH_BG">%s"FLAG_DOMAIN_DUMMY_IDX_STR"\">Location%s</A></TH>\n\n",
		  theAnchor[0], arrow[0], theAnchor[1], arrow[1]);
    sendString(buf);
    break;
  }

  sendString("</TR>\n\n");
}

/* ******************************* */

char* _getOSFlag(HostTraffic *el, char *elOsName, int showOsName, char *tmpStr, int tmpStrLen, char *file, int line) {
  /* Lengthen tmpString buffer - to handle long name given by nmap for Win2k
     Courtesy of Marcel Hauser <marcel_hauser@gmx.ch> */
  char *flagImg = "";
  char *theOsName;
  int i;

  if((el == NULL) && (elOsName == NULL)) return("");

  tmpStr[0] = '\0';

  if(elOsName != NULL)
    theOsName = elOsName;
  else {
    if(el->fingerprint == NULL)   return("");
    if(el->fingerprint[0] != ':') setHostFingerprint(el);
    if(el->fingerprint[0] != ':') return("");
    theOsName = &el->fingerprint[1];
  }

  if(theOsName[0] == '\0') return("");

  flagImg = NULL;

  for(i=0; osInfos[i].link != NULL; i++) {
    if(strstr(theOsName, osInfos[i].name) != NULL) {
      flagImg = osInfos[i].link;
      break;
    }
  }

  if(!showOsName) {
    if(flagImg != NULL) {
      safe_snprintf(file, line, tmpStr, tmpStrLen, "%s", flagImg);
    } else
      tmpStr[0] = '\0';
  } else {
    if(flagImg != NULL) {
      safe_snprintf(file, line, tmpStr, tmpStrLen, "%s&nbsp;[%s]", flagImg, theOsName);
    } else {
      safe_snprintf(file, line, tmpStr, tmpStrLen, "%s", theOsName);
    }
  }

  return(tmpStr);
}

/* ******************************* */

int sortHostFctn(const void *_a, const void *_b) {
  HostTraffic **a = (HostTraffic **)_a;
  HostTraffic **b = (HostTraffic **)_b;
  int rc, n_a, n_b;
  char *nameA, *nameB;

  if((a == NULL) && (b != NULL)) {
    traceEvent(CONST_TRACE_WARNING, "sortHostFctn() error (1)");
    return(1);
  } else if((a != NULL) && (b == NULL)) {
    traceEvent(CONST_TRACE_WARNING, "sortHostFctn() error (2)");
    return(-1);
  } else if((a == NULL) && (b == NULL)) {
    traceEvent(CONST_TRACE_WARNING, "sortHostFctn() error (3)");
    return(0);
  }

  switch(myGlobals.columnSort) {
  case 1:
    rc=cmpFctnResolvedName(a, b);
    return(rc);
    break;
  case 2:
    rc = addrcmp(&(*a)->hostIpAddress,&(*b)->hostIpAddress);
    return(rc);
    break;
  case 3:
    return(strcasecmp((*a)->ethAddressString, (*b)->ethAddressString));
  case 5:
    return(strcasecmp(getVendorInfo((*a)->ethAddress, 0),
		      getVendorInfo((*b)->ethAddress, 0)));
    break;
  case 6:
    if((*a)->nonIPTraffic == NULL) {
      nameA = "";
    } else {
    if((*a)->nonIPTraffic->nbHostName != NULL)
      nameA = (*a)->nonIPTraffic->nbHostName;
    else
      nameA = "";
    }

    if((*b)->nonIPTraffic == NULL) {
      nameB = "";
    } else {
      if((*b)->nonIPTraffic->nbHostName != NULL)
	nameB = (*b)->nonIPTraffic->nbHostName;
      else
	nameB = "";
    }

    return(strcasecmp(nameA, nameB));
    break;
  case 7:
    n_a = guessHops(*a);
    n_b = guessHops(*b);

    if(n_a < n_b)
      return(1);
    else if(n_a > n_b)
      return(-1);
    else
      return(0);
    break;
  case 8:
    n_a = (int)((*a)->totContactedSentPeers+(*a)->totContactedRcvdPeers);
    n_b = (int)((*b)->totContactedSentPeers+(*b)->totContactedRcvdPeers);

    if(n_a < n_b)
      return(1);
    else if(n_a > n_b)
      return(-1);
    else
      return(0);
    break;
  case 9:
    n_a = (int)((*a)->lastSeen-(*a)->firstSeen);
    n_b = (int)((*b)->lastSeen-(*b)->firstSeen);

    if(n_a < n_b)
      return(1);
    else if(n_a > n_b)
      return(-1);
    else
      return(0);
    break;
  case 10:
    n_a = (*a)->hostAS, n_b = (*b)->hostAS;

    if(n_a < n_b)
      return(1);
    else if(n_a > n_b)
      return(-1);
    else
      return(0);
    break;
  case 11:
   return(strcasecmp((*a)->community ? (*a)->community : "",
		      (*b)->community ? (*b)->community : ""));
    break;
  case 12:
    return(memcmp(&(*a)->flags, &(*b)->flags, sizeof(fd_set)));
    break;

  case CONST_VLAN_COLUMN_SORT:
    n_a = (*a)->vlanId, n_b = (*b)->vlanId;

    if(n_a < n_b)
      return(1);
    else if(n_a > n_b)
      return(-1);
    else
      return(0);
    break;
  case FLAG_DOMAIN_DUMMY_IDX:
    rc = cmpFctnLocationName(a, b);
    return(rc);
    break;
  case 4:
  default:
    if((*a)->actBandwidthUsage < (*b)->actBandwidthUsage)
      return(1);
    else if ((*a)->actBandwidthUsage > (*b)->actBandwidthUsage)
      return(-1);
    else
      return(0);
    break;
  }
}

/* ******************************* */

int cmpUsersTraffic(const void *_a, const void *_b) {
  UsersTraffic **a = (UsersTraffic **)_a;
  UsersTraffic **b = (UsersTraffic **)_b;
  Counter sum_a, sum_b;

  if((a == NULL) && (b != NULL)) {
    return(1);
  } else if((a != NULL) && (b == NULL)) {
    return(-1);
  } else if((a == NULL) && (b == NULL)) {
    return(0);
  }

  sum_a = (*a)->bytesSent + (*a)->bytesRcvd;
  sum_b = (*b)->bytesSent + (*b)->bytesRcvd;

  if(sum_a > sum_b)
    return(-1);
  else if (sum_a == sum_b)
    return(0);
  else
    return(1);
}

/* ******************************* */

int cmpProcesses(const void *_a, const void *_b) {
  ProcessInfo **a = (ProcessInfo **)_a;
  ProcessInfo **b = (ProcessInfo **)_b;

  if((a == NULL) && (b != NULL)) {
    return(1);
  } else if((a != NULL) && (b == NULL)) {
    return(-1);
  } else if((a == NULL) && (b == NULL)) {
    return(0);
  }

  switch(myGlobals.columnSort) {
  case 2: /* PID */
    if((*a)->pid == (*b)->pid)
      return(0);
    else if((*a)->pid < (*b)->pid)
      return(1);
    else return(-1);
    break;
  case 3: /* User */
    return(strcasecmp((*a)->user, (*b)->user));
    break;
  case 4: /* Sent */
    if((*a)->bytesSent.value == (*b)->bytesSent.value)
      return(0);
    else if((*a)->bytesSent.value < (*b)->bytesSent.value)
      return(1);
    else return(-1);
    break;
  case 5: /* Rcvd */
    if((*a)->bytesRcvd.value == (*b)->bytesRcvd.value)
      return(0);
    else if((*a)->bytesRcvd.value < (*b)->bytesRcvd.value)
      return(1);
    else return(-1);
    break;
  default: /* Process name */
    return(strcasecmp((*a)->command, (*b)->command));
  }
}

/* ******************************* */

static int cmpOSFctn(const void *_a, const void *_b) {
  OsNumInfo *a = (OsNumInfo *)_a;
  OsNumInfo *b = (OsNumInfo *)_b;

  if(a->num < b->num)
    return(1);
  else
    return(-1);
}

/* ******************************* */

int cmpFctnLocationName(const void *_a, const void *_b) {
  HostTraffic **a = (HostTraffic **)_a;
  HostTraffic **b = (HostTraffic **)_b;
  char *c_a = "", *c_b = "";

  if((a != NULL) && (*a != NULL)
     && (*a)->geo_ip && ((*a)->geo_ip->country_code != NULL))
    c_a = (*a)->geo_ip->country_code;

  if((b != NULL) && (*b != NULL)
     && (*b)->geo_ip && ((*b)->geo_ip->country_code != NULL))
    c_b = (*b)->geo_ip->country_code;

  return(strcmp(c_a, c_b));
}

/* ******************************* */

int cmpFctn(const void *_a, const void *_b) {
  HostTraffic **a = (HostTraffic **)_a;
  HostTraffic **b = (HostTraffic **)_b;
  Counter a_=0, b_=0;
  float fa_=0, fb_=0;
  short floatCompare = 0, columnProtoId;

  if((a == NULL) && (b != NULL)) {
    traceEvent(CONST_TRACE_WARNING, "cmpFctn() error (1)");
    return(1);
  } else if((a != NULL) && (b == NULL)) {
    traceEvent(CONST_TRACE_WARNING, "cmpFctn() error (2)");
    return(-1);
  } else if((a == NULL) && (b == NULL)) {
    traceEvent(CONST_TRACE_WARNING, "cmpFctn() error (3)");
    return(0);
  } else if((*a == NULL) && (*b != NULL)) {
    traceEvent(CONST_TRACE_WARNING, "cmpFctn() error (4)");
    return(1);
  } else if((*a != NULL) && (*b == NULL)) {
    traceEvent(CONST_TRACE_WARNING, "cmpFctn() error (5)");
    return(-1);
  } else if((*a == NULL) && (*b == NULL)) {
    traceEvent(CONST_TRACE_WARNING, "cmpFctn() error (6)");
    return(0);
  }

  if(myGlobals.columnSort == FLAG_HOST_DUMMY_IDX) {
    return(cmpFctnResolvedName(a, b));
  } else if(myGlobals.columnSort == FLAG_DOMAIN_DUMMY_IDX) {
    int rc;

    fillDomainName(*a); fillDomainName(*b);

#ifdef DEBUG
    traceEvent(CONST_TRACE_INFO, "%s='%s'/'%s' - %s='%s'/'%s'",
	   (*a)->hostResolvedName,
	   (*a)->ip2ccValue, (*a)->dnsDomainValue,
	   (*b)->hostResolvedName,
	   (*b)->ip2ccValue, (*b)->dnsDomainValue
	   );
#endif

    rc = cmpFctnLocationName(a, b);
    return(rc);
  }

#ifdef DEBUG
  traceEvent(CONST_TRACE_INFO,
	     "reportKind=%d/columnSort=%d/numIpProtosToMonitor=%d\n",
	     myGlobals.reportKind, myGlobals.columnSort,
	     myGlobals.numIpProtosToMonitor);
#endif

  switch(myGlobals.reportKind) {
  case SORT_DATA_RECEIVED_PROTOS:
    switch(myGlobals.columnSort) {
    case 0:
      a_ = (*a)->bytesRcvd.value, b_ = (*b)->bytesRcvd.value;
      break;
    case 1:
      a_ = (*a)->tcpRcvdLoc.value + (*a)->tcpRcvdFromRem.value;
      b_ = (*b)->tcpRcvdLoc.value + (*b)->tcpRcvdFromRem.value;
      break;
    case 2:
      a_ = (*a)->udpRcvdLoc.value + (*a)->udpRcvdFromRem.value;
      b_ = (*b)->udpRcvdLoc.value + (*b)->udpRcvdFromRem.value;
      break;
    case 3:
      a_ = (*a)->icmpRcvd.value, b_ = (*b)->icmpRcvd.value;
      break;
    case 4:
      a_ = (*a)->icmp6Rcvd.value, b_ = (*b)->icmp6Rcvd.value;
      break;
    case 7:
      a_ = (*a)->ipsecRcvd.value;
      b_ = (*b)->ipsecRcvd.value;
      break;
    case 8:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->arp_rarpRcvd.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->arp_rarpRcvd.value;
      break;
    case 11:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->netbiosRcvd.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->netbiosRcvd.value;
      break;
    case 13:
      a_ = (*a)->greRcvd.value;
      b_ = (*b)->greRcvd.value;
      break;
    case 14:
      a_ = (*a)->ipv6BytesRcvd.value, b_ = (*b)->ipv6BytesRcvd.value;
      break;
    case 15:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->stpRcvd.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->stpRcvd.value;
      break;
    case 16:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->otherRcvd.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->otherRcvd.value;
      break;
    default:
      if((myGlobals.columnSort >= BASE_PROTOS_IDX)
	 && (myGlobals.columnSort < (BASE_PROTOS_IDX+myGlobals.numIpProtosList))) {
	int idx = myGlobals.columnSort-BASE_PROTOS_IDX;

	if((*a)->ipProtosList && (*a)->ipProtosList[idx])
	  a_ = (*a)->ipProtosList[idx]->rcvd.value;
	else
	  a_ = 0;

	if((*b)->ipProtosList && (*b)->ipProtosList[idx])
	  b_ = (*b)->ipProtosList[idx]->rcvd.value;
	else
	  b_ = 0;
      }
      break;
    }
    break;

  case SORT_DATA_RECEIVED_IP:
    columnProtoId = myGlobals.columnSort - 1;
    if((columnProtoId != -1) && (columnProtoId <= myGlobals.l7.numSupportedProtocols)) {
      if(columnProtoId <= 0) {
	a_ = b_ = 0;
      } else {
	a_ = (*a)->l7.traffic[columnProtoId-1].bytesRcvd;
	b_ = (*b)->l7.traffic[columnProtoId-1].bytesRcvd;
      }
    } else {
      a_ = (*a)->ipv4BytesRcvd.value, b_ = (*b)->ipv4BytesRcvd.value;
    }
    break;

  case SORT_DATA_RECEIVED_THPT:
    switch(myGlobals.columnSort) {
    case 1:
      fa_ = (*a)->actualRcvdThpt, fb_ = (*b)->actualRcvdThpt, floatCompare = 1;
      break;
    case 2:
      fa_ = (*a)->averageRcvdThpt, fb_ = (*b)->averageRcvdThpt, floatCompare = 1;
      break;
    case 3:
      fa_ = (*a)->peakRcvdThpt, fb_ = (*b)->peakRcvdThpt, floatCompare = 1;
      break;
    }
    break;

  case SORT_DATA_RCVD_HOST_TRAFFIC:
  case SORT_DATA_SENT_HOST_TRAFFIC:
  case SORT_DATA_HOST_TRAFFIC:
    /* Nothing */
    break;
  case SORT_DATA_SENT_PROTOS:
    switch(myGlobals.columnSort) {
    case 0:
      a_ = (*a)->bytesSent.value, b_ = (*b)->bytesSent.value;
      break;
    case 1:
      a_ = (*a)->tcpSentLoc.value + (*a)->tcpSentRem.value;
      b_ = (*b)->tcpSentLoc.value + (*b)->tcpSentRem.value;
      break;
    case 2:
      a_ = (*a)->udpSentLoc.value + (*a)->udpSentRem.value;
      b_ = (*b)->udpSentLoc.value + (*b)->udpSentRem.value;
      break;
    case 3:
      a_ = (*a)->icmpSent.value, b_ = (*b)->icmpSent.value;
      break;
    case 4:
      a_ = (*a)->icmp6Sent.value, b_ = (*b)->icmp6Sent.value;
      break;
    case 7:
      a_ = (*a)->ipsecSent.value;
      b_ = (*b)->ipsecSent.value;
      break;
    case 8:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->arp_rarpSent.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->arp_rarpSent.value;
      break;
    case 11:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->netbiosSent.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->netbiosSent.value;
      break;
    case 13:
      a_ = (*a)->greSent.value;
      b_ = (*b)->greSent.value;
      break;
    case 14:
      a_ = (*a)->ipv6BytesSent.value, b_ = (*b)->ipv6BytesSent.value;
      break;
    case 15:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->stpSent.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->stpSent.value;
      break;
    case 16:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->otherSent.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->otherSent.value;
      break;
    default:
      if((myGlobals.columnSort >= BASE_PROTOS_IDX)
	 && (myGlobals.columnSort < (BASE_PROTOS_IDX+myGlobals.numIpProtosList))) {
	int idx = myGlobals.columnSort-BASE_PROTOS_IDX;

	if((*a)->ipProtosList && (*a)->ipProtosList[idx])
	  a_ = (*a)->ipProtosList[idx]->sent.value;
	else
	  a_ = 0;

	if((*b)->ipProtosList && (*b)->ipProtosList[idx])
	  b_ = (*b)->ipProtosList[idx]->sent.value;
	else
	  b_ = 0;
      }
      break;
    }
    break;

  case SORT_DATA_SENT_IP:
    columnProtoId = myGlobals.columnSort - 1;
    if((columnProtoId != -1) && (columnProtoId <= myGlobals.l7.numSupportedProtocols)) {
      if(columnProtoId <= 0) {
	a_ = b_ = 0;
      } else {
	a_ = (*a)->l7.traffic[columnProtoId-1].bytesSent;
        b_ = (*b)->l7.traffic[columnProtoId-1].bytesSent;
      }
    } else {
      a_ = (*a)->ipv4BytesSent.value, b_ = (*b)->ipv4BytesSent.value;
    }
    break;

  case SORT_DATA_SENT_THPT:
    switch(myGlobals.columnSort) {
    case 1:
      fa_ = (*a)->actualSentThpt, fb_ = (*b)->actualSentThpt, floatCompare = 1;
      break;
    case 2:
      fa_ = (*a)->averageSentThpt, fb_ = (*b)->averageSentThpt, floatCompare = 1;
      break;
    case 3:
      fa_ = (*a)->peakSentThpt, fb_ = (*b)->peakSentThpt, floatCompare = 1;
      break;
    }
    break;

  case TRAFFIC_STATS:
    /* Nothing */
    break;
  case SORT_DATA_PROTOS:
    switch(myGlobals.columnSort) {
    case 0:
      a_ = (*a)->bytesRcvd.value+(*a)->bytesSent.value, b_ = (*b)->bytesRcvd.value+(*b)->bytesSent.value;
      break;
    case 1:
      a_ = (*a)->tcpRcvdLoc.value + (*a)->tcpRcvdFromRem.value +
           (*a)->tcpSentLoc.value + (*a)->tcpSentRem.value;
      b_ = (*b)->tcpRcvdLoc.value + (*b)->tcpRcvdFromRem.value +
           (*b)->tcpSentLoc.value + (*b)->tcpSentRem.value;
      break;
    case 2:
      a_ = (*a)->udpRcvdLoc.value + (*a)->udpRcvdFromRem.value +
           (*a)->udpSentLoc.value + (*a)->udpSentRem.value;
      b_ = (*b)->udpRcvdLoc.value + (*b)->udpRcvdFromRem.value +
           (*b)->udpSentLoc.value + (*b)->udpSentRem.value;
      break;
    case 3:
      a_ = (*a)->icmpRcvd.value+(*a)->icmpSent.value, b_ = (*b)->icmpRcvd.value+(*b)->icmpSent.value;
      break;
    case 4:
      a_ = (*a)->icmp6Rcvd.value+(*a)->icmp6Sent.value, b_ = (*b)->icmp6Rcvd.value+(*b)->icmp6Sent.value;
      break;
    case 5:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->stpSent.value+(*a)->nonIPTraffic->stpRcvd.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->stpSent.value+(*b)->nonIPTraffic->stpRcvd.value;
      break;
    case 7:
      a_ = (*a)->ipsecSent.value+(*a)->ipsecRcvd.value;
      b_ = (*b)->ipsecSent.value+(*b)->ipsecRcvd.value;
      break;
    case 8:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->arp_rarpSent.value+(*a)->nonIPTraffic->arp_rarpRcvd.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->arp_rarpSent.value+(*b)->nonIPTraffic->arp_rarpRcvd.value;
      break;
    case 11:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->netbiosSent.value+(*a)->nonIPTraffic->netbiosRcvd.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->netbiosSent.value+(*b)->nonIPTraffic->netbiosRcvd.value;
      break;
    case 13:
      a_ = (*a)->greSent.value + (*a)->greRcvd.value;
      b_ = (*b)->greSent.value + (*b)->greRcvd.value;
      break;
    case 14:
      a_ = (*a)->ipv6BytesRcvd.value+(*a)->ipv6BytesSent.value, b_ = (*b)->ipv6BytesRcvd.value+(*b)->ipv6BytesSent.value;
      break;
    case 15:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->stpSent.value+(*a)->nonIPTraffic->stpRcvd.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->stpSent.value+(*b)->nonIPTraffic->stpRcvd.value;
      break;
    case 16:
      a_ = (*a)->nonIPTraffic == NULL ? 0 : (*a)->nonIPTraffic->otherSent.value+(*a)->nonIPTraffic->otherRcvd.value;
      b_ = (*b)->nonIPTraffic == NULL ? 0 : (*b)->nonIPTraffic->otherSent.value+(*b)->nonIPTraffic->otherRcvd.value;
      break;
    default:
      if((myGlobals.columnSort >= BASE_PROTOS_IDX)
	 && (myGlobals.columnSort < (BASE_PROTOS_IDX+myGlobals.numIpProtosList))) {
	int idx = myGlobals.columnSort-BASE_PROTOS_IDX;

	if(((*a)->ipProtosList == NULL) && ((*b)->ipProtosList != NULL)) return(1);
	else if(((*a)->ipProtosList != NULL) && ((*b)->ipProtosList == NULL)) return(-1);

	if((*a)->ipProtosList && (*a)->ipProtosList[idx])
	  a_ = (*a)->ipProtosList[idx]->sent.value +(*a)->ipProtosList[idx]->rcvd.value;
	else
	  a_ = 0;

	if((*b)->ipProtosList && (*b)->ipProtosList[idx])
	  b_ = (*b)->ipProtosList[idx]->sent.value +(*b)->ipProtosList[idx]->rcvd.value;
	else
	  b_ = 0;
      }
      break;
    }
    break;

  case SORT_DATA_IP:
    columnProtoId = myGlobals.columnSort - 1;

    if((columnProtoId != -1) && (columnProtoId <= myGlobals.l7.numSupportedProtocols)) {
      if(columnProtoId <= 0) {
        a_ = b_ = 0;
      } else {
	a_ = (*a)->l7.traffic[columnProtoId-1].bytesSent + (*a)->l7.traffic[columnProtoId-1].bytesRcvd;
	b_ = (*b)->l7.traffic[columnProtoId-1].bytesSent + (*b)->l7.traffic[columnProtoId-1].bytesRcvd;
      }
    } else {
      a_ = (*a)->ipv4BytesRcvd.value+(*a)->ipv4BytesSent.value;
      b_ = (*b)->ipv4BytesRcvd.value+(*b)->ipv4BytesSent.value;
    }
    break;

  case SORT_DATA_THPT:
    switch(myGlobals.columnSort) {
    case 1:
      fa_ = (*a)->actualThpt;
      fb_ = (*b)->actualThpt;
      floatCompare = 1;
      break;
    case 2:
      fa_ = (*a)->averageThpt;
      fb_ = (*b)->averageThpt;
      floatCompare = 1;
      break;
    case 3:
      fa_ = (*a)->peakThpt;
      fb_ = (*b)->peakThpt;
      floatCompare = 1;
      break;
    }
    break;
  }

  if(floatCompare == 0) {
#if 0
    traceEvent(CONST_TRACE_INFO, "[1] %s=%lu - %s=%lu",
	       (*a)->hostResolvedName, (unsigned long)a_,
	       (*b)->hostResolvedName, (unsigned long)b_);
#endif

    if(a_ < b_) {
      return(1);
    } else if (a_ > b_) {
      return(-1);
    }
  } else {
#if 0
    traceEvent(CONST_TRACE_INFO, "[2] %s=%lu - %s=%lu",
	       (*a)->hostResolvedName, (unsigned long)fa_,
	       (*b)->hostResolvedName, (unsigned long)fb_);
#endif

    if(fa_ < fb_) {
      return(1);
    } else if (fa_ > fb_) {
      return(-1);
    }
  }

  return(0);
}

/* ******************************* */

int cmpMulticastFctn(const void *_a, const void *_b) {
  HostTraffic **a = (HostTraffic **)_a;
  HostTraffic **b = (HostTraffic **)_b;
  int rc;

  if((a == NULL) && (b != NULL)) {
    traceEvent(CONST_TRACE_WARNING, "cmpMulticastFctn() error (1)");
    return(1);
  } else if((a != NULL) && (b == NULL)) {
    traceEvent(CONST_TRACE_WARNING, "cmpMulticastFctn() error (2)");
    return(-1);
  } else if((a == NULL) && (b == NULL)) {
    traceEvent(CONST_TRACE_WARNING, "cmpMulticastFctn() error (3)");
    return(0);
  }

  switch(myGlobals.columnSort) {
  case 2:
    if((*a)->pktsMulticastSent.value < (*b)->pktsMulticastSent.value)
      return(1);
    else if ((*a)->pktsMulticastSent.value > (*b)->pktsMulticastSent.value)
      return(-1);
    else
      return(0);
    break; /* NOTREACHED */
  case 3:
    if((*a)->bytesMulticastSent.value < (*b)->bytesMulticastSent.value)
      return(1);
    else if ((*a)->bytesMulticastSent.value > (*b)->bytesMulticastSent.value)
      return(-1);
    else
      return(0);
    break; /* NOTREACHED */
  case 4:
    if((*a)->pktsMulticastRcvd.value < (*b)->pktsMulticastRcvd.value)
      return(1);
    else if ((*a)->pktsMulticastRcvd.value > (*b)->pktsMulticastRcvd.value)
      return(-1);
    else
      return(0);
    break; /* NOTREACHED */
  case 5:
    if((*a)->bytesMulticastRcvd.value < (*b)->bytesMulticastRcvd.value)
      return(1);
    else if ((*a)->bytesMulticastRcvd.value > (*b)->bytesMulticastRcvd.value)
      return(-1);
    else
      return(0);
    break; /* NOTREACHED */

  default:
    rc=cmpFctnResolvedName(a, b);
    return(rc);
  }
}

/* *********************************** */

static char* getBgPctgColor(float pctg) {
  if(pctg == 0)
    return(TD_BG);
  else if(pctg <= CONST_PCTG_LOW)     /* < 25%       */
    return(CONST_CONST_PCTG_LOW_COLOR);
  else if(pctg <= CONST_PCTG_MID)     /* 25% <=> 75% */
    return(CONST_CONST_PCTG_MID_COLOR);
  else                          /* > 75%       */
    return(CONST_PCTG_HIGH_COLOR);
}

/* ******************************* */

void printHostThtpShort(HostTraffic *el, int reportType, u_int hourId)
{
  int i, j;
  Counter tc;
  char buf[64];

  if(el->trafficDistribution == NULL) return;

  for(i=0, tc=0; i<24; i++) {
    switch(reportType) {
        case SORT_DATA_RCVD_HOST_TRAFFIC:
	  tc += el->trafficDistribution->last24HoursBytesRcvd[i].value;
	  break;
    case SORT_DATA_SENT_HOST_TRAFFIC:
      tc += el->trafficDistribution->last24HoursBytesSent[i].value;
      break;
    case SORT_DATA_HOST_TRAFFIC:
    case TRAFFIC_STATS:
      tc += el->trafficDistribution->last24HoursBytesRcvd[i].value +
	el->trafficDistribution->last24HoursBytesSent[i].value;
      break;
    }
  }

  j = hourId;
  for(i=0; i<24; i++) {
    float pctg=0;

    j = j % 24;
    if(tc > 0) {
      switch(reportType) {
      case SORT_DATA_RCVD_HOST_TRAFFIC:
	pctg = (float)(el->trafficDistribution->last24HoursBytesRcvd[j].value*100)/(float)tc;
	break;
      case SORT_DATA_SENT_HOST_TRAFFIC:
	pctg = (float)(el->trafficDistribution->last24HoursBytesSent[j].value*100)/(float)tc;
	break;
      case SORT_DATA_HOST_TRAFFIC:
      case TRAFFIC_STATS:
	pctg = ( (float)(el->trafficDistribution->last24HoursBytesRcvd[j].value*100) +
		 (float)(el->trafficDistribution->last24HoursBytesSent[j].value*100) ) / (float)tc;
	break;
      }
    }

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT %s>&nbsp;</TD>",
		getBgPctgColor(pctg));
    sendString(buf);
    if (!j) {
        j = 23;
    }
    else {
        j--;
    }
  }
}

/* ******************************* */

int cmpHostsFctn(const void *_a, const void *_b) {
  struct hostTraffic **a = (struct hostTraffic **)_a;
  struct hostTraffic **b = (struct hostTraffic **)_b;
  Counter a_=0, b_=0;

  switch(myGlobals.columnSort) {
  case 2: /* IP Address */
    return(addrcmp(&(*a)->hostIpAddress, &(*b)->hostIpAddress));
    break;

  case 3: /* Data Sent */
    switch(myGlobals.sortFilter) {
      case FLAG_REMOTE_TO_LOCAL_ACCOUNTING:
	a_ = (*a)->bytesSentLoc.value;
	b_ = (*b)->bytesSentLoc.value;
	break;
      case FLAG_LOCAL_TO_REMOTE_ACCOUNTING:
	a_ = (*a)->bytesSentRem.value;
	b_ = (*b)->bytesSentRem.value;
	break;
      case FLAG_LOCAL_TO_LOCAL_ACCOUNTING:
	a_ = (*a)->bytesSentLoc.value;
	b_ = (*b)->bytesSentLoc.value;
	break;
      }
    if(a_ < b_) return(1); else if (a_ > b_) return(-1); else return(0);
    break;

  case 4: /* Data Rcvd */
      switch(myGlobals.sortFilter) {
      case FLAG_REMOTE_TO_LOCAL_ACCOUNTING:
	a_ = (*a)->bytesRcvdLoc.value;
	b_ = (*b)->bytesRcvdLoc.value;
	break;
      case FLAG_LOCAL_TO_REMOTE_ACCOUNTING:
	a_ = (*a)->bytesRcvdFromRem.value;
	b_ = (*b)->bytesRcvdFromRem.value;
	break;
      case FLAG_LOCAL_TO_LOCAL_ACCOUNTING:
	a_ = (*a)->bytesRcvdLoc.value;
	b_ = (*b)->bytesRcvdLoc.value;
	break;
      }
    if(a_ < b_) return(1); else if (a_ > b_) return(-1); else return(0);
    break;

  default: /* Host Name */
    return(cmpFctnResolvedName(a, b));
  }

  return(-1);
}

/* ************************************ */

int printPacketStats(HostTraffic *el, int actualDeviceId) {
  char buf[LEN_GENERAL_WORK_BUFFER];
  char formatBuf[32];
  int headerSent = 0, rc = 0;
  char *tableHeader = "<center><TABLE BORDER=0 "TABLE_DEFAULTS">\n<TR><TD>";

  /* *********************** */

  if(el->secHostPkts != NULL) {
    if(((el->secHostPkts->rejectedTCPConnSent.value.value+
	 el->secHostPkts->rejectedTCPConnRcvd.value.value+
	 el->secHostPkts->establishedTCPConnSent.value.value+
	 el->secHostPkts->establishedTCPConnRcvd.value.value+
	 el->secHostPkts->synPktsSent.value.value+
	 el->secHostPkts->synPktsRcvd.value.value) > 0)) {

      if(!headerSent) { sendString(tableHeader); headerSent = 1; }

      sendString("<CENTER>\n"
		 ""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=100%>"
		 "<TR "TR_ON" "DARK_BG"><TH "TH_BG">TCP Connections</TH>"
		 "<TH "TH_BG" COLSPAN=2>Directed to</TH>"
		 "<TH "TH_BG" COLSPAN=2>Rcvd From</TH></TR>\n\n");

      if((el->secHostPkts->synPktsSent.value.value+el->secHostPkts->synPktsRcvd.value.value) > 0) {
	sendString("<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Attempted</TH>");
	formatUsageCounter(el->secHostPkts->synPktsSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->synPktsRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->establishedTCPConnSent.value.value+el->secHostPkts->establishedTCPConnRcvd.value.value) > 0) {
	sendString("<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Established</TH>");
	formatUsageCounter(el->secHostPkts->establishedTCPConnSent, el->secHostPkts->synPktsSent.value.value, actualDeviceId);
	formatUsageCounter(el->secHostPkts->establishedTCPConnRcvd, el->secHostPkts->synPktsRcvd.value.value, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->terminatedTCPConnServer.value.value + el->secHostPkts->terminatedTCPConnClient.value.value) > 0) {
	sendString("<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Terminated</TH>");
	formatUsageCounter(el->secHostPkts->terminatedTCPConnServer, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->terminatedTCPConnClient, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->rejectedTCPConnSent.value.value+el->secHostPkts->rejectedTCPConnRcvd.value.value) > 0) {
	sendString("<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Rejected</TH>");
	formatUsageCounter(el->secHostPkts->rejectedTCPConnSent, el->secHostPkts->synPktsSent.value.value, actualDeviceId);
	formatUsageCounter(el->secHostPkts->rejectedTCPConnRcvd, el->secHostPkts->synPktsRcvd.value.value, actualDeviceId);
	sendString("</TR>\n\n");
      }
    }

    /* *********************** */

    if((el->secHostPkts->synPktsSent.value.value+el->secHostPkts->synPktsRcvd.value.value
	+el->secHostPkts->rstAckPktsSent.value.value+el->secHostPkts->rstAckPktsRcvd.value.value
	+el->secHostPkts->rstPktsSent.value.value+el->secHostPkts->rstPktsRcvd.value.value
	+el->secHostPkts->synFinPktsSent.value.value+el->secHostPkts->synFinPktsRcvd.value.value
	+el->secHostPkts->finPushUrgPktsSent.value.value+el->secHostPkts->finPushUrgPktsRcvd.value.value
	+el->secHostPkts->nullPktsSent.value.value+el->secHostPkts->nullPktsRcvd.value.value) > 0) {

      if(!headerSent) {
	sendString(tableHeader);
	headerSent = 1;

	sendString("<CENTER>\n"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=100%>");
	sendString("<TR "TR_ON" "DARK_BG"><TH "TH_BG">TCP Flags</TH>"
		   "<TH "TH_BG" COLSPAN=2>Pkts&nbsp;Sent</TH>"
		   "<TH "TH_BG" COLSPAN=2>Pkts&nbsp;Rcvd</TH></TR>\n\n");
      }

      if((el->secHostPkts->synPktsSent.value.value+el->secHostPkts->synPktsRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">SYN</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->synPktsSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->synPktsRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->rstAckPktsSent.value.value+el->secHostPkts->rstAckPktsRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">RST|ACK</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->rstAckPktsSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->rstAckPktsRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->rstPktsSent.value.value+el->secHostPkts->rstPktsRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">RST</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->rstPktsSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->rstPktsRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->synFinPktsSent.value.value+el->secHostPkts->synFinPktsRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">SYN|FIN</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->synFinPktsSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->synFinPktsRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->finPushUrgPktsSent.value.value+el->secHostPkts->finPushUrgPktsRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">FIN|PUSH|URG</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->finPushUrgPktsSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->finPushUrgPktsRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->nullPktsSent.value.value+el->secHostPkts->nullPktsRcvd.value.value) > 0) {
	sendString("<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">NULL</TH>");
	formatUsageCounter(el->secHostPkts->nullPktsSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->nullPktsRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }
    }

    /* *********************** */

    if(((el->secHostPkts->ackXmasFinSynNullScanSent.value.value+el->secHostPkts->ackXmasFinSynNullScanRcvd.value.value
	 +el->secHostPkts->udpToClosedPortSent.value.value
	 +el->secHostPkts->udpToClosedPortRcvd.value.value
	 +el->secHostPkts->udpToDiagnosticPortSent.value.value
	 +el->secHostPkts->udpToDiagnosticPortRcvd.value.value
	 +el->secHostPkts->tcpToDiagnosticPortSent.value.value
	 +el->secHostPkts->tcpToDiagnosticPortRcvd.value.value
	 +el->secHostPkts->tinyFragmentSent.value.value
	 +el->secHostPkts->tinyFragmentRcvd.value.value
	 +el->secHostPkts->icmpFragmentSent.value.value
	 +el->secHostPkts->icmpFragmentRcvd.value.value
	 +el->secHostPkts->overlappingFragmentSent.value.value
	 +el->secHostPkts->overlappingFragmentRcvd.value.value
	 +el->secHostPkts->closedEmptyTCPConnSent.value.value
	 +el->secHostPkts->closedEmptyTCPConnRcvd.value.value
	 +el->secHostPkts->icmpPortUnreachSent.value.value
	 +el->secHostPkts->icmpPortUnreachRcvd.value.value
	 +el->secHostPkts->icmpHostNetUnreachSent.value.value
	 +el->secHostPkts->icmpHostNetUnreachRcvd.value.value
	 +el->secHostPkts->icmpProtocolUnreachSent.value.value
	 +el->secHostPkts->icmpProtocolUnreachRcvd.value.value
	 +el->secHostPkts->icmpAdminProhibitedSent.value.value
	 +el->secHostPkts->icmpAdminProhibitedRcvd.value.value
	 +el->secHostPkts->malformedPktsSent.value.value
	 +el->secHostPkts->malformedPktsRcvd.value.value
	 ) > 0)) {

      if(!headerSent) {
	sendString(tableHeader);
	headerSent = 1;

	sendString("<CENTER>\n"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=100%>");
	sendString("<TR "TR_ON" "DARK_BG"><TH "TH_BG">Anomaly</TH>"
		   "<TH "TH_BG" COLSPAN=2>Pkts&nbsp;Sent&nbsp;to</TH>"
		   "<TH "TH_BG" COLSPAN=2>Pkts&nbsp;Rcvd&nbsp;from</TH>"
		   "</TR>\n\n");
      }

      if((el->secHostPkts->ackXmasFinSynNullScanSent.value.value+el->secHostPkts->ackXmasFinSynNullScanRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">ACK/XMAS/SYN/FIN/NULL Scan</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->ackXmasFinSynNullScanSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->ackXmasFinSynNullScanRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->udpToClosedPortSent.value.value+
	  el->secHostPkts->udpToClosedPortRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">UDP Pkt to Closed Port</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->udpToClosedPortSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->udpToClosedPortRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->udpToDiagnosticPortSent.value.value+
	  el->secHostPkts->udpToDiagnosticPortRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">UDP Pkt Disgnostic Port</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->udpToDiagnosticPortSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->udpToDiagnosticPortRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->tcpToDiagnosticPortSent.value.value+
	  el->secHostPkts->tcpToDiagnosticPortRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">TCP Pkt Disgnostic Port</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->tcpToDiagnosticPortSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->tcpToDiagnosticPortRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->tinyFragmentSent.value.value+
	  el->secHostPkts->tinyFragmentRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">Tiny Fragments</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->tinyFragmentSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->tinyFragmentRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->icmpFragmentSent.value.value+
	  el->secHostPkts->icmpFragmentRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">ICMP Fragments</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->icmpFragmentSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->icmpFragmentRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->overlappingFragmentSent.value.value+
	  el->secHostPkts->overlappingFragmentRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">Overlapping Fragments</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->overlappingFragmentSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->overlappingFragmentRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->closedEmptyTCPConnSent.value.value+
	  el->secHostPkts->closedEmptyTCPConnRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">Closed Empty TCP Conn.</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->closedEmptyTCPConnSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->closedEmptyTCPConnRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }


      if((el->secHostPkts->icmpPortUnreachSent.value.value+
	  el->secHostPkts->icmpPortUnreachRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">ICMP Port Unreachable</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->icmpPortUnreachSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->icmpPortUnreachRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->icmpHostNetUnreachSent.value.value+
	  el->secHostPkts->icmpHostNetUnreachRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">ICMP Net Unreachable</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->icmpHostNetUnreachSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->icmpHostNetUnreachRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->icmpProtocolUnreachSent.value.value+
	  el->secHostPkts->icmpProtocolUnreachRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">ICMP Protocol Unreachable</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->icmpProtocolUnreachSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->icmpProtocolUnreachRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->icmpAdminProhibitedSent.value.value+
	  el->secHostPkts->icmpAdminProhibitedRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">ICMP Administratively Prohibited</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->icmpAdminProhibitedSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->icmpAdminProhibitedRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }

      if((el->secHostPkts->malformedPktsSent.value.value+
	  el->secHostPkts->malformedPktsRcvd.value.value) > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">Malformed Pkts</TH>",
		      getRowColor());
	sendString(buf);
	formatUsageCounter(el->secHostPkts->malformedPktsSent, 0, actualDeviceId);
	formatUsageCounter(el->secHostPkts->malformedPktsRcvd, 0, actualDeviceId);
	sendString("</TR>\n\n");
      }
    }
  }

  if(headerSent) {
    sendString("</TABLE>"TABLE_OFF"<P>\n");
    sendString("</CENTER>\n");
    sendString("</TD></TR>\n</TABLE>"TABLE_OFF"</CENTER>");
    rc = 1;
  }

  if(el->nonIPTraffic) {
    if((el->nonIPTraffic->arpReqPktsSent.value
	+ el->nonIPTraffic->arpReplyPktsSent.value
	+ el->nonIPTraffic->arpReplyPktsRcvd.value) > 0) {

      rc = 1;
      //if(headerSent) sendString(tableHeader);
	sendString("<CENTER>\n"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=100%>");
	sendString("<TR "TR_ON" "DARK_BG">"
		   "<TH "TH_BG">ARP</TH>"
		   "<TH "TH_BG">Packets</TH>"
		   "</TR>\n\n");

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		    "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT>Request Sent</TH>"
		    "<TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		    getRowColor(),
		    formatPkts(el->nonIPTraffic->arpReqPktsSent.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		    "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT>Reply Rcvd</TH>"
		    "<TD "TD_BG" ALIGN=RIGHT>%s (%.1f %%)</TD></TR>\n",
		    getRowColor(),
		    formatPkts(el->nonIPTraffic->arpReplyPktsRcvd.value, formatBuf, sizeof(formatBuf)),
		    ((el->nonIPTraffic->arpReqPktsSent.value > 0) ?
		     (float)((el->nonIPTraffic->arpReplyPktsRcvd.value*100)/(float)el->nonIPTraffic->arpReqPktsSent.value) : 0));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT>Reply Sent</TH>"
		    "<TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		    getRowColor(),
		    formatPkts(el->nonIPTraffic->arpReplyPktsSent.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);

      sendString("</TABLE>"TABLE_OFF"<P>\n</CENTER>\n");
    }
  }

  return(rc);
}

/* ************************************ */

void hostReport(int idx, char *hostName, int vlanId, int sortedColumn) {
  HostTraffic *el;

  urlFixupFromRFC1945Inplace(hostName);

#ifdef URL_DEBUG
  traceEvent(CONST_TRACE_INFO, "Searching hostname: '%s'", hostName);
#endif

  for(el=getFirstHost(myGlobals.actualReportDeviceId);
      el != NULL; el = getNextHost(myGlobals.actualReportDeviceId, el)) {

    if((el != myGlobals.broadcastEntry)
       && (el->hostNumIpAddress != NULL)
       && ((el->vlanId <= 0) || (el->vlanId == vlanId))
       && ((strcmp(el->hostNumIpAddress, hostName) == 0)
	   || (strcmp(el->ethAddressString, hostName) == 0))) {
      break;
    }
  } /* for */

  if(el) {
    if(el->community && (!isAllowedCommunity(el->community))) {
      returnHTTPpageBadCommunity();
    } else {
      sendHTTPHeader(FLAG_HTTP_TYPE_HTML, 0, 1);

      switch(idx) {
      case 0:
	hostTrafficDistrib(el, sortedColumn);
	break;
      case 1:
	hostFragmentDistrib(el, sortedColumn);
	break;
      case 2:
	hostTotalFragmentDistrib(el, sortedColumn);
	break;
      case 3:
	hostTimeTrafficDistribution(el, sortedColumn);
	break;
      case 4:
	hostIPTrafficDistrib(el, sortedColumn);
	break;
      case 5:
	sendHTTPHeader(FLAG_HTTP_TYPE_HTML, 0, 0);
	createHostMap(el);
	break;
      }
    }
  }
}

/* ************************************ */

void printHostFragmentStats(HostTraffic *el, int actualDeviceId) {
  Counter totalSent, totalRcvd;
  char buf[LEN_GENERAL_WORK_BUFFER];
  char linkName[LEN_GENERAL_WORK_BUFFER/2];

  totalSent = el->tcpFragmentsSent.value + el->udpFragmentsSent.value + el->icmpFragmentsSent.value;
  totalRcvd = el->tcpFragmentsRcvd.value + el->udpFragmentsRcvd.value + el->icmpFragmentsRcvd.value;

  if((totalSent == 0) && (totalRcvd == 0))
    return;

  sendString("<CENTER>\n"
	     ""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">"
	     "<TR "DARK_BG"><TH "TH_BG" WIDTH=100>Protocol</TH>"
	     "<TH "TH_BG" WIDTH=200 COLSPAN=2>Data&nbsp;Sent</TH>"
	     "<TH "TH_BG" WIDTH=200 COLSPAN=2>Data&nbsp;Rcvd</TH></TR>\n\n");

  printTableDoubleEntry(buf, sizeof(buf), "TCP", CONST_COLOR_1, 
			(float)el->tcpFragmentsSent.value/1024,
			100*((float)SD(el->tcpFragmentsSent.value, totalSent)),
			(float)el->tcpFragmentsRcvd.value/1024,
			100*((float)SD(el->tcpFragmentsRcvd.value, totalRcvd)));

  printTableDoubleEntry(buf, sizeof(buf), "UDP", CONST_COLOR_1, (float)el->udpFragmentsSent.value/1024,
			100*((float)SD(el->udpFragmentsSent.value, totalSent)),
			(float)el->udpFragmentsRcvd.value/1024,
			100*((float)SD(el->udpFragmentsRcvd.value, totalRcvd)));

  printTableDoubleEntry(buf, sizeof(buf), "ICMP", CONST_COLOR_1, (float)el->icmpFragmentsSent.value/1024,
			100*((float)SD(el->icmpFragmentsSent.value, totalSent)),
			(float)el->icmpFragmentsRcvd.value/1024,
			100*((float)SD(el->icmpFragmentsRcvd.value, totalRcvd)));

  if((totalSent > 0) || (totalRcvd > 0)) {
    int vlanId;

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT>Fragment Distribution</TH>",
		  getRowColor());
    sendString(buf);

    if(el->hostNumIpAddress[0] != '\0') {
      strncpy(linkName, el->hostNumIpAddress, sizeof(linkName));
    } else {
      strncpy(linkName, el->ethAddressString, sizeof(linkName));
    }

    /* For Ethernet and IPv6 addresses */
    urlFixupToRFC1945Inplace(linkName);

    if(el->vlanId > 0) {
      vlanId = -el->vlanId;
    } else
      vlanId = 0;

    if(totalSent > 0) {
      sendString("<TD "TD_BG" ALIGN=RIGHT COLSPAN=2 BGCOLOR=white>");
      hostReport(1, linkName, vlanId, 1);
      sendString("</TD>");
    } else {
      sendString("<TD "TD_BG" ALIGN=RIGHT COLSPAN=2>&nbsp;</TD>");
    }

    if(totalRcvd > 0) {
      sendString("<TD "TD_BG" ALIGN=RIGHT COLSPAN=2 BGCOLOR=white>");
      hostReport(1, linkName, vlanId, 1);
      sendString("</TD>");
    } else {
      sendString("<TD "TD_BG" ALIGN=RIGHT COLSPAN=2>&nbsp;</TD>");
    }

    sendString("</TD></TR>\n");

    /* ***************************************** */

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s>"
		  "<TH "TH_BG" ALIGN=LEFT>IP Fragment Distribution</TH>",
		  getRowColor());
    sendString(buf);

    if(totalSent > 0) {
      sendString("<TD "TD_BG" ALIGN=RIGHT COLSPAN=2 BGCOLOR=white>");
      hostReport(2, linkName, vlanId, 1);
      sendString("</TD>");
    } else {
      sendString("<TD "TD_BG" ALIGN=RIGHT COLSPAN=2>&nbsp;</TD>");
    }

    if(totalRcvd > 0) {
      sendString("<TD "TD_BG" ALIGN=RIGHT COLSPAN=2 BGCOLOR=white>");
      hostReport(2, linkName, vlanId, 0);
      sendString("</TD>");
    } else {
      sendString("<TD "TD_BG" ALIGN=RIGHT COLSPAN=2>&nbsp;</TD>");
    }

    sendString("</TD></TR>\n");
  }
  
  sendString("</TABLE>"TABLE_OFF"<P>\n");
  sendString("</CENTER>\n");
}

/* ************************************ */

static char* sap2name(u_int16_t proto, char *sap, int sap_len) {
  switch(proto) {
  case SAP_NULL:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "NULL LSAP");
    break;
  case SAP_LLC_SLMGMT:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "LLC Sub-Layer Management");
    break;
  case SAP_SNA_PATHCTRL:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "SNA Path Control");
    break;
  case SAP_IP:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "TCP/IP");
    break;
  case SAP_SNA1:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "SNA");
    break;
  case SAP_SNA2:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "SNA");
    break;
  case SAP_PROWAY_NM_INIT:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "PROWAY (IEC955) Network Management and Initialization");
    break;
  case SAP_TI:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "Texas Instruments");
    break;
  case SAP_BPDU:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "Spanning Tree BPDU");
    break;
  case SAP_RS511:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "EIA RS-511 Manufacturing Message Service");
    break;
  case SAP_X25:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "ISO 8208 (X.25 over 802.2)");
    break;
  case 0x7F:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "ISO 802.2");
    break;
  case SAP_XNS:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "XNS");
    break;
  case SAP_BACNET:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "BACnet");
    break;
  case SAP_NESTAR:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "Nestar");
    break;
  case SAP_PROWAY_ASLM:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "PROWAY (IEC955) Active Station List Maintenance");
    break;
  case SAP_ARP:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "ARP");
    break;
  case SAP_SNAP:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "SNAP");
    break;
  case SAP_VINES1:
  case SAP_VINES2:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "Banyan Vines");
    break;
  case SAP_NETWARE:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "NetWare");
    break;
  case SAP_NETBIOS:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "NetBIOS");
    break;
  case SAP_IBMNM:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "IBM Net Management");
    break;
  case SAP_HPEXT:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "HP Extended LLC");
    break;
  case SAP_UB:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "Ungermann-Bass");
    break;
  case SAP_RPL:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "Remote Program Load");
    break;
  case SAP_OSINL:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "ISO Network Layer");
    break;
  case SAP_GLOBAL:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "Global LSAP");
    break;
  default:
    safe_snprintf(__FILE__, __LINE__, sap, sap_len, "0x%X", proto);
    break;
  }

  return(sap);
}

/* ************************************ */

static void printUnknownProto(UnknownProto proto) {
  char buf[64];

  switch(proto.protoType) {
  case 1:
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<li>Ethernet Type: 0x%04X\n", proto.proto.ethType);
    break;
  case 2:
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<li>SAP: DSAP=0x%02X/SSAP=0x%02X\n",
	     proto.proto.sapType.dsap, proto.proto.sapType.ssap);
    break;
  case 3:
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<li>IP Protocol: 0x%d\n", proto.proto.ipType);
    break;
  default:
    return;
  }

  sendString(buf);
}

/* ************************************ */

void printHostTrafficStats(HostTraffic *el, int actualDeviceId) {
  Counter totalSent, totalRcvd;
  Counter actTotalSent, actTotalRcvd;
  char buf[LEN_GENERAL_WORK_BUFFER];
  char linkName[LEN_GENERAL_WORK_BUFFER/2];
  int i, idx;
  ProtocolsList *protoList;

  totalSent = el->tcpSentLoc.value+el->tcpSentRem.value+el->udpSentLoc.value+el->udpSentRem.value;
  totalSent += el->icmpSent.value+el->icmp6Sent.value+el->ipv6BytesSent.value;
  totalSent += el->greSent.value + el->ipsecSent.value;

  if(el->nonIPTraffic != NULL)
    totalSent += el->nonIPTraffic->arp_rarpSent.value +
      el->nonIPTraffic->netbiosSent.value+el->nonIPTraffic->stpSent.value+el->nonIPTraffic->otherSent.value;

  totalRcvd = el->tcpRcvdLoc.value+el->tcpRcvdFromRem.value;
  totalRcvd += el->udpRcvdLoc.value+el->udpRcvdFromRem.value;
  totalRcvd += el->icmpRcvd.value+el->icmp6Rcvd.value+el->ipv6BytesRcvd.value;
  totalRcvd += el->greRcvd.value + el->ipsecRcvd.value;

  if(el->nonIPTraffic != NULL)
    totalRcvd += el->nonIPTraffic->arp_rarpRcvd.value +
      el->nonIPTraffic->netbiosRcvd.value+el->nonIPTraffic->stpRcvd.value+el->nonIPTraffic->otherRcvd.value;

  protoList = myGlobals.ipProtosList;
  idx = 0;

  while(protoList != NULL) {
    if(el->ipProtosList[idx] != NULL) {
      totalSent += el->ipProtosList[idx]->sent.value;
      totalRcvd += el->ipProtosList[idx]->rcvd.value;
    }
    idx++, protoList = protoList->next;
  }

  actTotalSent = el->tcpSentLoc.value+el->tcpSentRem.value;
  actTotalRcvd = el->tcpRcvdLoc.value+el->tcpRcvdFromRem.value;

  if(el->trafficDistribution) {
    sendString("\n\n<div id=\"tabs-21\">\n");
    printHostHourlyTraffic(el);
    sendString("\n</div>\n");
  }

  if(el->secHostPkts || el->nonIPTraffic) {
    sendString("\n\n<!------ DIV ------>\n");
    sendString("\n\n<div id=\"tabs-22\">\n");
    if(printPacketStats(el, actualDeviceId) == 0)
      sendString("\n<i>"CONST_NO_DATA_YET"</i>");
    sendString("\n</div>\n");
  }

  /*
  if((totalSent == 0) && (totalRcvd == 0))
    return;
  */

  sendString("\n\n<!------ DIV ------>\n");
  sendString("\n\n<div id=\"tabs-23\">\n");

  sendString("<CENTER>\n"
	     ""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=80%%><TR "DARK_BG"><TH "TH_BG" WIDTH=100>Protocol</TH>"
	     "<TH "TH_BG" WIDTH=200 COLSPAN=2>Data&nbsp;Sent</TH>"
	     "<TH "TH_BG" WIDTH=200 COLSPAN=2>Data&nbsp;Rcvd</TH></TR>\n\n");

  printTableDoubleEntry(buf, sizeof(buf), "TCP", CONST_COLOR_1, (float)actTotalSent/1024,
			100*((float)SD(actTotalSent, totalSent)),
			(float)actTotalRcvd/1024,
			100*((float)SD(actTotalRcvd, totalRcvd)));

  actTotalSent = el->udpSentLoc.value+el->udpSentRem.value;
  actTotalRcvd = el->udpRcvdLoc.value+el->udpRcvdFromRem.value;

  printTableDoubleEntry(buf, sizeof(buf), "UDP", CONST_COLOR_1, (float)actTotalSent/1024,
			100*((float)SD(actTotalSent, totalSent)),
			(float)actTotalRcvd/1024,
			100*((float)SD(actTotalRcvd, totalRcvd)));

  printTableDoubleEntry(buf, sizeof(buf), "ICMP", CONST_COLOR_1, (float)el->icmpSent.value/1024,
			100*((float)SD(el->icmpSent.value, totalSent)),
			(float)el->icmpRcvd.value/1024,
			100*((float)SD(el->icmpRcvd.value, totalRcvd)));

  printTableDoubleEntry(buf, sizeof(buf), "ICMPv6", CONST_COLOR_1, (float)el->icmp6Sent.value/1024,
                        100*((float)SD(el->icmp6Sent.value, totalSent)),
                        (float)el->icmp6Rcvd.value/1024,
                        100*((float)SD(el->icmp6Rcvd.value, totalRcvd)));

  printTableDoubleEntry(buf, sizeof(buf), "IPv6", CONST_COLOR_1, (float)el->ipv6BytesSent.value/1024,
			100*((float)SD(el->ipv6BytesSent.value, totalSent)),
			(float)el->ipv6BytesRcvd.value/1024,
			100*((float)SD(el->ipv6BytesRcvd.value, totalRcvd)));

  printTableDoubleEntry(buf, sizeof(buf), "GRE", CONST_COLOR_1, (float)el->greSent.value/1024,
			100*((float)SD(el->greSent.value, totalSent)),
			(float)el->greRcvd.value/1024,
			100*((float)SD(el->greRcvd.value, totalRcvd)));

  printTableDoubleEntry(buf, sizeof(buf), "IPsec", CONST_COLOR_1,
			(float)el->ipsecSent.value/1024,
			100*((float)SD(el->ipsecSent.value, totalSent)),
			(float)el->ipsecRcvd.value/1024,
			100*((float)SD(el->ipsecRcvd.value, totalRcvd)));

  if(el->nonIPTraffic != NULL) {
    printTableDoubleEntry(buf, sizeof(buf), "(R)ARP", CONST_COLOR_1, (float)el->nonIPTraffic->arp_rarpSent.value/1024,
			  100*((float)SD(el->nonIPTraffic->arp_rarpSent.value, totalSent)),
			  (float)el->nonIPTraffic->arp_rarpRcvd.value/1024,
			  100*((float)SD(el->nonIPTraffic->arp_rarpRcvd.value, totalRcvd)));

    printTableDoubleEntry(buf, sizeof(buf), "NetBios", CONST_COLOR_1, (float)el->nonIPTraffic->netbiosSent.value/1024,
			  100*((float)SD(el->nonIPTraffic->netbiosSent.value, totalSent)),
			  (float)el->nonIPTraffic->netbiosRcvd.value/1024,
			  100*((float)SD(el->nonIPTraffic->netbiosRcvd.value, totalRcvd)));

    printTableDoubleEntry(buf, sizeof(buf), "STP", CONST_COLOR_1, (float)el->nonIPTraffic->stpSent.value/1024,
			  100*((float)SD(el->nonIPTraffic->stpSent.value, totalSent)),
			  (float)el->nonIPTraffic->stpRcvd.value/1024,
			  100*((float)SD(el->nonIPTraffic->stpRcvd.value, totalRcvd)));

    printTableDoubleEntry(buf, sizeof(buf), "Other (Non IP)", CONST_COLOR_1, (float)el->nonIPTraffic->otherSent.value/1024,
			  100*((float)SD(el->nonIPTraffic->otherSent.value, totalSent)),
			  (float)el->nonIPTraffic->otherRcvd.value/1024,
			  100*((float)SD(el->nonIPTraffic->otherRcvd.value, totalRcvd)));
  }

  {
    protoList = myGlobals.ipProtosList;
    idx = 0;

    while(protoList != NULL) {
      printTableDoubleEntry(buf, sizeof(buf), protoList->protocolName, CONST_COLOR_1,
			    el->ipProtosList[idx] != NULL ? (float)el->ipProtosList[idx]->sent.value/1024 : 0,
			    el->ipProtosList[idx] != NULL ? 100*((float)SD(el->ipProtosList[idx]->sent.value, totalSent)) : 0,
			    el->ipProtosList[idx] != NULL ? (float)el->ipProtosList[idx]->rcvd.value/1024 : 0,
			    el->ipProtosList[idx] != NULL ? 100*((float)SD(el->ipProtosList[idx]->rcvd.value, totalRcvd)) : 0);
      idx++, protoList = protoList->next;
    }
  }

  {
    totalSent = el->tcpSentLoc.value+el->tcpSentRem.value+
      el->udpSentLoc.value+el->udpSentRem.value+
      el->icmpSent.value+el->icmp6Sent.value+el->ipv6BytesSent.value+
      el->greSent.value+el->ipsecSent.value;

    totalRcvd = el->tcpRcvdLoc.value+el->tcpRcvdFromRem.value+
      el->udpRcvdLoc.value+el->udpRcvdFromRem.value+
      el->icmpRcvd.value+el->icmp6Rcvd.value+el->ipv6BytesRcvd.value+
      el->greRcvd.value+el->ipsecRcvd.value;

    if(el->nonIPTraffic) {
      totalSent += el->nonIPTraffic->stpSent.value+
	el->nonIPTraffic->arp_rarpSent.value+
	el->nonIPTraffic->netbiosSent.value+el->nonIPTraffic->otherSent.value;

      totalRcvd += el->nonIPTraffic->stpRcvd.value+
	el->nonIPTraffic->arp_rarpRcvd.value+
	el->nonIPTraffic->netbiosRcvd.value+el->nonIPTraffic->otherRcvd.value;
    }

    protoList = myGlobals.ipProtosList;
    idx = 0;

    while(protoList != NULL) {
      if(el->ipProtosList[idx] != NULL) {
	totalSent += el->ipProtosList[idx]->sent.value;
	totalRcvd += el->ipProtosList[idx]->rcvd.value;
      }

      idx++, protoList = protoList->next;
    }

    if((totalSent > 0) || (totalRcvd > 0)) {
      Counter sent, rcvd;
      int headerAlreadySent;

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		    "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">Protocol Distribution</TH>",
		    getRowColor());
      sendString(buf);

      if(el->hostNumIpAddress[0] != '\0')
        strncpy(linkName, el->hostNumIpAddress, sizeof(linkName));
      else
        strncpy(linkName, el->ethAddressString, sizeof(linkName));

      /* For Ethernet and Ipv6 addresses */
      urlFixupToRFC1945Inplace(linkName);

     if(totalSent > 0) {
       sendString("<TD WIDTH=250 "TD_BG" ALIGN=RIGHT COLSPAN=2 BGCOLOR=white>");
       hostReport(0, linkName, el->vlanId, 1);
       sendString("</TD>");
      } else {
	sendString("<TD width=250 "TD_BG" ALIGN=RIGHT COLSPAN=2 WIDTH=250>&nbsp;</TD>");
      }

      if(totalRcvd > 0) {
       sendString("<TD WIDTH=250 "TD_BG" ALIGN=RIGHT COLSPAN=2 BGCOLOR=white>");
       hostReport(0, linkName, el->vlanId, 0);
       sendString("</TD>");
      } else {
	sendString("<TD "TD_BG" ALIGN=RIGHT COLSPAN=2 WIDTH=250>&nbsp;</TD>");
      }

      sendString("</TD></TR>\n");

      sent = rcvd = 0;
      headerAlreadySent = 0;

      for(i=0; i<myGlobals.l7.numSupportedProtocols; i++)
	sent += el->l7.traffic[i].bytesSent, rcvd += el->l7.traffic[i].bytesRcvd;

      for(i=0; i<myGlobals.l7.numSupportedProtocols; i++) {
	if(el->l7.traffic[i].bytesSent || el->l7.traffic[i].bytesRcvd) {
	  if(!headerAlreadySent) {
	    sendString("<TR "DARK_BG"><TH "TH_BG" WIDTH=100>L7 Protocol</TH>"
		       "<TH "TH_BG" WIDTH=200 COLSPAN=2>Data&nbsp;Sent</TH>"
		       "<TH "TH_BG" WIDTH=200 COLSPAN=2>Data&nbsp;Rcvd</TH></TR>\n\n");

	    headerAlreadySent = 1;
	  }

	  printTableDoubleEntry(buf, sizeof(buf),
				getProtoName(0, i), CONST_COLOR_1,
				(float)el->l7.traffic[i].bytesSent/1024,
				100*((float)SD(el->l7.traffic[i].bytesSent, sent)),
				(float)el->l7.traffic[i].bytesRcvd/1024,
				100*((float)SD(el->l7.traffic[i].bytesRcvd, rcvd)));
	}
      }

      if(sent || rcvd) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">"
		      "IP Distribution</TH>",
		      getRowColor());
	sendString(buf);

	if(sent > 0) {
	  sendString("<TD "TD_BG" ALIGN=RIGHT COLSPAN=2 BGCOLOR=white>");
	  hostReport(4, linkName, el->vlanId, 1);
	  sendString("</TD>");
	} else
	  sendString("<TD "TD_BG" COLSPAN=2 WIDTH=250>&nbsp;</TD>");

	if(rcvd > 0) {
	  sendString("<TD "TD_BG" ALIGN=RIGHT COLSPAN=2 BGCOLOR=white>");
	  hostReport(4, linkName, el->vlanId, 0);
	  sendString("</TD>");
	} else
	  sendString("<TD "TD_BG" COLSPAN=2 WIDTH=250>&nbsp;</TD>");

	sendString("</TR>\n");
      }
    }
  }

  sendString("</TABLE>"TABLE_OFF"<P>\n");
  sendString("</CENTER>\n");

  /* ************************************** */

  if(el->nonIpProtoTrafficInfos != NULL) {
    NonIpProtoTrafficInfo *nonIp = el->nonIpProtoTrafficInfos;

    printSectionTitle("Non IP Protocol Distribution");

    sendString("<CENTER>\n"
	       ""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "DARK_BG" "DARK_BG"><TH "TH_BG" WIDTH=100>Protocol</TH>"
	       "<TH "TH_BG" WIDTH=200 COLSPAN=2>Data&nbsp;Sent</TH>"
	       "<TH "TH_BG" WIDTH=200 COLSPAN=2>Data&nbsp;Rcvd</TH></TR>\n\n");

    while(nonIp != NULL) {
      char buf1[64];

      printTableDoubleEntry(buf, sizeof(buf), sap2name(nonIp->protocolId, buf1, sizeof(buf1)),
			    CONST_COLOR_1, (float)nonIp->sentBytes.value/1024,
			    100*((float)SD(nonIp->sentBytes.value,
					   el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->otherSent.value)),
			    (float)nonIp->rcvdBytes.value/1024,
			    100*((float)SD(nonIp->rcvdBytes.value,
					   el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->otherRcvd.value)));

      nonIp = nonIp->next;
    }

    sendString("</TABLE>"TABLE_OFF"<P>\n");
    sendString("</CENTER>\n");
  }

 /* ************************************** */

  if(el->nonIPTraffic != NULL) {
    if(el->nonIPTraffic->unknownProtoSent || el->nonIPTraffic->unknownProtoRcvd) {
      printSectionTitle("Unknown Protocols");

      sendString("<CENTER>\n"
		 ""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "DARK_BG">"
		 "<TH "TH_BG" WIDTH=200>Data&nbsp;Sent</TH>"
		 "<TH "TH_BG" WIDTH=200>Data&nbsp;Rcvd</TH></TR>\n\n");

      if(el->nonIPTraffic->unknownProtoSent == NULL) {
	sendString("\n<TR><TH "TH_BG">&nbsp;</TH>");
      } else {
	sendString("\n<TR><TH "TH_BG" ALIGN=LEFT>");
	for(i=0; i<MAX_NUM_UNKNOWN_PROTOS; i++)
	  printUnknownProto(el->nonIPTraffic->unknownProtoSent[i]);
	sendString("&nbsp;</TH>");
      }

      if(el->nonIPTraffic->unknownProtoRcvd == NULL) {
	sendString("<TH "TH_BG">&nbsp;</TH></TR>\n");
      } else {
	sendString("<TH "TH_BG" ALIGN=LEFT>");
	for(i=0; i<MAX_NUM_UNKNOWN_PROTOS; i++)
	  printUnknownProto(el->nonIPTraffic->unknownProtoRcvd[i]);
	sendString("&nbsp;</TH></TR>\n");
      }

      sendString("</TABLE>"TABLE_OFF"<P>\n");
      sendString("</CENTER>\n");
    }
  }

  sendString("\n</div>\n");
}

/* ************************************ */

void printIcmpv6Stats(HostTraffic *el) {
  char buf[LEN_GENERAL_WORK_BUFFER], formatBuf[32], formatBuf1[32];

  printSectionTitle("ICMPv6 Traffic\n");
  sendString("<CENTER><TABLE BORDER=1 "TABLE_DEFAULTS">\n");
  sendString("<TR "TR_ON" "DARK_BG"><th>Type</th>"
             "<TH "TH_BG" ALIGN=LEFT>Pkt&nbsp;Sent</TH>"
             "<TH "TH_BG" ALIGN=LEFT>Pkt&nbsp;Rcvd</TH></TR>\n\n");

  if(el->icmpInfo->icmpMsgSent[ICMP6_ECHO_REQUEST].value+el->icmpInfo->icmpMsgRcvd[ICMP6_ECHO_REQUEST].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Echo Request</TH>"
                "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
                formatPkts(el->icmpInfo->icmpMsgSent[ICMP6_ECHO_REQUEST].value, formatBuf, sizeof(formatBuf)),
                formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP6_ECHO_REQUEST].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ICMP6_ECHO_REPLY].value+el->icmpInfo->icmpMsgRcvd[ICMP6_ECHO_REPLY].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Echo Reply</TH>"
                "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
                formatPkts(el->icmpInfo->icmpMsgSent[ICMP6_ECHO_REPLY].value, formatBuf, sizeof(formatBuf)),
                formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP6_ECHO_REPLY].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ICMP6_DST_UNREACH].value+el->icmpInfo->icmpMsgRcvd[ICMP6_DST_UNREACH].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Unreach</TH>"
                "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
                formatPkts(el->icmpInfo->icmpMsgSent[ICMP6_DST_UNREACH].value, formatBuf, sizeof(formatBuf)),
                formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP6_DST_UNREACH].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ND_REDIRECT].value+el->icmpInfo->icmpMsgRcvd[ND_REDIRECT].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Redirect</TH>"
                "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
                formatPkts(el->icmpInfo->icmpMsgSent[ND_REDIRECT].value, formatBuf, sizeof(formatBuf)),
                formatPkts(el->icmpInfo->icmpMsgRcvd[ND_REDIRECT].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ND_ROUTER_ADVERT].value+el->icmpInfo->icmpMsgRcvd[ND_ROUTER_ADVERT].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Router Advertisement</TH>"
                "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
                formatPkts(el->icmpInfo->icmpMsgSent[ND_ROUTER_ADVERT].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ND_ROUTER_ADVERT].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ND_ROUTER_SOLICIT].value+el->icmpInfo->icmpMsgRcvd[ND_ROUTER_SOLICIT].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Router solicitation</TH>"
                "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
                formatPkts(el->icmpInfo->icmpMsgSent[ND_ROUTER_SOLICIT].value, formatBuf, sizeof(formatBuf)),
                formatPkts(el->icmpInfo->icmpMsgRcvd[ND_ROUTER_SOLICIT].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ND_NEIGHBOR_SOLICIT].value+el->icmpInfo->icmpMsgRcvd[ND_NEIGHBOR_SOLICIT].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Neighbor solicitation</TH>"
                "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
                formatPkts(el->icmpInfo->icmpMsgSent[ND_NEIGHBOR_SOLICIT].value, formatBuf, sizeof(formatBuf)),
                formatPkts(el->icmpInfo->icmpMsgRcvd[ND_NEIGHBOR_SOLICIT].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ND_NEIGHBOR_ADVERT].value+el->icmpInfo->icmpMsgRcvd[ND_NEIGHBOR_ADVERT].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Neighbor advertisment</TH>"
                "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
                formatPkts(el->icmpInfo->icmpMsgSent[ND_NEIGHBOR_ADVERT].value, formatBuf, sizeof(formatBuf)),
                formatPkts(el->icmpInfo->icmpMsgRcvd[ND_NEIGHBOR_ADVERT].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  sendString("</TABLE>"TABLE_OFF"</CENTER>\n");
}

/* ******************************************** */

void printIcmpv4Stats(HostTraffic *el) {
  char buf[LEN_GENERAL_WORK_BUFFER];
  char formatBuf[32], formatBuf1[32];

  sendString("<CENTER><TABLE BORDER=1 "TABLE_DEFAULTS">\n");
  sendString("<TR "TR_ON" "DARK_BG"><th>Type</th>"
	     "<TH "TH_BG" ALIGN=LEFT>Pkt&nbsp;Sent</TH>"
	     "<TH "TH_BG" ALIGN=LEFT>Pkt&nbsp;Rcvd</TH></TR>\n\n");

  if(el->icmpInfo->icmpMsgSent[ICMP_ECHO].value+el->icmpInfo->icmpMsgRcvd[ICMP_ECHO].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Echo Request</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_ECHO].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_ECHO].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ICMP_ECHOREPLY].value+el->icmpInfo->icmpMsgRcvd[ICMP_ECHOREPLY].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Echo Reply</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_ECHOREPLY].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_ECHOREPLY].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ICMP_UNREACH].value+el->icmpInfo->icmpMsgRcvd[ICMP_UNREACH].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Unreach</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_UNREACH].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_UNREACH].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ICMP_REDIRECT].value+el->icmpInfo->icmpMsgRcvd[ICMP_REDIRECT].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Redirect</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_REDIRECT].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_REDIRECT].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

   if(el->icmpInfo->icmpMsgSent[ICMP_ROUTERADVERT].value+el->icmpInfo->icmpMsgRcvd[ICMP_ROUTERADVERT].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Router Advertisement</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_ROUTERADVERT].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_ROUTERADVERT].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

   if(el->icmpInfo->icmpMsgSent[ICMP_TIMXCEED].value+el->icmpInfo->icmpMsgRcvd[ICMP_TIMXCEED].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Time Exceeded</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_TIMXCEED].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_TIMXCEED].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ICMP_PARAMPROB].value+el->icmpInfo->icmpMsgRcvd[ICMP_PARAMPROB].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Parameter Problem</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_PARAMPROB].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_PARAMPROB].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ICMP_MASKREQ].value+el->icmpInfo->icmpMsgRcvd[ICMP_MASKREQ].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Network Mask Request</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_MASKREQ].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_MASKREQ].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ICMP_MASKREPLY].value+el->icmpInfo->icmpMsgRcvd[ICMP_MASKREPLY].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Network Mask Reply</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_MASKREPLY].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_MASKREPLY].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ICMP_SOURCE_QUENCH].value+el->icmpInfo->icmpMsgRcvd[ICMP_SOURCE_QUENCH].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Source Quench</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_SOURCE_QUENCH].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_SOURCE_QUENCH].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ICMP_TIMESTAMP].value+el->icmpInfo->icmpMsgRcvd[ICMP_TIMESTAMP].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Timestamp</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_TIMESTAMP].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_TIMESTAMP].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ICMP_INFO_REQUEST].value+el->icmpInfo->icmpMsgRcvd[ICMP_INFO_REQUEST].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Info Request</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_INFO_REQUEST].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_INFO_REQUEST].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if(el->icmpInfo->icmpMsgSent[ICMP_INFO_REPLY].value+el->icmpInfo->icmpMsgRcvd[ICMP_INFO_REPLY].value > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT>Info Reply</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		formatPkts(el->icmpInfo->icmpMsgSent[ICMP_INFO_REPLY].value, formatBuf, sizeof(formatBuf)),
		formatPkts(el->icmpInfo->icmpMsgRcvd[ICMP_INFO_REPLY].value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  /************ ICMPv6 info*********************/

  sendString("</TABLE>"TABLE_OFF"</CENTER>\n");
}

/* ************************************ */

void printHostIcmpStats(HostTraffic *el){
  int family;

  if(el->icmpInfo == NULL) return;
  family = el->hostIpAddress.hostFamily;
  if (family == AF_INET)
    printIcmpv4Stats(el);
  else if (family == AF_INET6)
    printIcmpv6Stats(el);
}

/* ************************************ */

void printHostHTTPVirtualHosts(HostTraffic *el, int actualDeviceId) {
  char buf[LEN_GENERAL_WORK_BUFFER];
  char formatBuf[32], formatBuf1[32];

  if((el->protocolInfo != NULL) && (el->protocolInfo->httpVirtualHosts != NULL)) {
    VirtualHostList *list = el->protocolInfo->httpVirtualHosts;

    sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=100%>"
	       "<TR "TR_ON" "DARK_BG"><TH "TH_BG">Virtual Host</TH>"
	       "<TH "TH_BG">Sent</TH><TH "TH_BG">Rcvd</TH></TR>\n\n");

    while(list != NULL) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=CENTER>%s&nbsp;</TD>"
		  "<TD "TD_BG" ALIGN=CENTER>%s&nbsp;</TD></TR>\n\n",
		  getRowColor(), list->virtualHostName,
		  formatBytes(list->bytesSent.value, 1, formatBuf, sizeof(formatBuf)),
		  formatBytes(list->bytesRcvd.value, 1, formatBuf1, sizeof(formatBuf1)));
      sendString(buf);
      list = list->next;
    }
    sendString("</TABLE>"TABLE_OFF"\n");
    sendString("<H5>NOTE: The above table is not updated in realtime but when connections are terminated.</H5>\n");
  }
}

/* ************************************ */

HostTraffic* quickHostLink(HostSerialIndex theSerialIdx, int deviceId, HostTraffic *el) {
  HostSerial theSerial;

  if(cmpSerial(&theSerialIdx, &myGlobals.broadcastEntry->serialHostIndex)) {
    memcpy(el, myGlobals.broadcastEntry, sizeof(HostTraffic));
    return(el);
  } else if(cmpSerial(&theSerialIdx, &myGlobals.otherHostEntry->serialHostIndex)) {
    memcpy(el, myGlobals.otherHostEntry, sizeof(HostTraffic));
    return(0);
  } else
    getHostSerialFromId(theSerialIdx, &theSerial);

  memset(el, 0, sizeof(HostTraffic));
  copySerial(&el->serialHostIndex, &theSerialIdx);

  if((theSerial.serialType == SERIAL_IPV4) || (theSerial.serialType == SERIAL_IPV6)) {
    /* IP */
    char buf[LEN_GENERAL_WORK_BUFFER];

    addrcpy(&el->hostIpAddress,&theSerial.value.ipSerial.ipAddress);
    el->vlanId = theSerial.value.ipSerial.vlanId;

    strncpy(el->hostNumIpAddress,
	    _addrtostr(&el->hostIpAddress, buf, sizeof(buf)),
	    sizeof(el->hostNumIpAddress));

    // FIX -- Update address TODO
  } else {
    /* MAC */
    char *ethAddr;
    char etherbuf[LEN_ETHERNET_ADDRESS_DISPLAY];

    memcpy(el->ethAddress, theSerial.value.ethSerial.ethAddress, LEN_ETHERNET_ADDRESS);
    el->vlanId = theSerial.value.ethSerial.vlanId;
    ethAddr = etheraddr_string(el->ethAddress, etherbuf);
    strncpy(el->ethAddressString, ethAddr, sizeof(el->ethAddressString));
    if (el->hostIpAddress.hostFamily == AF_INET)
      el->hostIp4Address.s_addr = 0x1234; /* dummy */
  }

  return(el);
}

/* ************************************ */

void printHostContactedPeers(HostTraffic *el, int actualDeviceId) {
  char buf[LEN_GENERAL_WORK_BUFFER], hostLinkBuf[3*LEN_GENERAL_WORK_BUFFER];
  HostTraffic *theHost;
  u_int8_t header_sent;

  if((el->pktsSent.value == 0) && (el->pktsRcvd.value == 0)) return;

  sendString("<CENTER>\n<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR>\n");

  if(el->pktsSent.value > 0) {
    header_sent = 0;

    for(theHost=getFirstHost(actualDeviceId);
	theHost != NULL;
	theHost = getNextHost(actualDeviceId, theHost)) {
      if((theHost->l2Host == el->l2Host) && (theHost->hostIpAddress.hostFamily == el->hostIpAddress.hostFamily)) {
 	int val = CM_PointEst(el->sent_to_matrix, theHost->serialHostIndex);

	if(val > 0) {

	  if(!header_sent) {
	    header_sent = 1;
	    sendString("<TD "TD_BG" VALIGN=TOP>"
		       "<div style=\"height:120px;width:500px;overflow-x:hidden;overflow-y:scroll;\">"
		       TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=480>"
		       "<TR "TR_ON" "DARK_BG"><TH "TH_BG">Sent To</TH><TH "TH_BG"># Contacts</TH></TR>\n\n");
	  }

	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			"<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
			"<TD "TD_BG" ALIGN=RIGHT>%u&nbsp;</TD></TR>\n\n",
			getRowColor(), makeHostLink(theHost, 0, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			val);

	  sendString(buf);
	}
      }
    }

    sendString("</TABLE>"TABLE_OFF);

    if(header_sent) sendString("\n</div>\n");
    sendString("</TD>\n");
  }

  /* ************************** */

  if(el->pktsRcvd.value > 0) {
    header_sent = 0;

    for(theHost=getFirstHost(actualDeviceId);
	theHost != NULL;
	theHost = getNextHost(actualDeviceId, theHost)) {
      if((theHost->l2Host == el->l2Host) && (theHost->hostIpAddress.hostFamily == el->hostIpAddress.hostFamily)) {
	int val = CM_PointEst(el->recv_from_matrix, theHost->serialHostIndex);

	if(val > 0) {
	  if(!header_sent) {
	    header_sent = 1;
	    sendString("<TD "TD_BG" VALIGN=TOP>"
		       "<div style=\"height:120px;width:500px;overflow-x:hidden;overflow-y:scroll;\">"
		       TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=480>"
		       "<TR "TR_ON" "DARK_BG"><TH "TH_BG">Received From</TH>"
		       "<TH "TH_BG"># Contacts</TH></TR>\n\n");
	  }

	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			"<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
			"<TD "TD_BG" ALIGN=RIGHT>%u&nbsp;</TD></TR>\n\n",
			getRowColor(), makeHostLink(theHost, 0, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)), val);

	  sendString(buf);
	}
      }
    }

    sendString("</TABLE>"TABLE_OFF);
    if(header_sent) sendString("\n</div>\n");
    sendString("</TD>\n");

    /* ***************************************************** */
  }

  sendString("</TR></TABLE>"TABLE_OFF"\n");
  sendString("</CENTER>\n");
}

/* ************************************ */

/* Function below courtesy of Andreas Pfaller <apfaller@yahoo.com.au> */

char *getSessionState(IPSession *session) {
  switch (session->sessionState) {
  case FLAG_STATE_SYN:
    return("Syn&nbsp;Sent");
  case FLAG_STATE_SYN_ACK:
    return("Rcvd&nbsp;Syn/Ack");
  case FLAG_STATE_ACTIVE:
    return("Active");
  case FLAG_STATE_FIN1_ACK0:
    return("Fin1&nbsp;Ack0");
  case FLAG_STATE_FIN1_ACK1:
    return("Fin1&nbsp;Ack1");
  case FLAG_STATE_FIN2_ACK0:
    return("Fin2&nbsp;Ack0");
  case FLAG_STATE_FIN2_ACK1:
    return("Fin2&nbsp;Ack1");
  case FLAG_STATE_FIN2_ACK2:
    return("Fin2&nbsp;Ack2");
  case FLAG_STATE_TIMEOUT:
    return("Timeout");
  case FLAG_STATE_END:
    return("End");
  }

 return("*Unknown*");
}

/* ******************************* */
/*
   Return codes:

   OK          0
   Minor       1
   Warning     2
   Error       3
*/

u_short isHostHealthy(HostTraffic *el) {
  if(hasDuplicatedMac(el) || hasSentIpDataOnZeroPort(el))
    return(3);

  if(hasWrongNetmask(el))
    return(2);

  if((el->totContactedSentPeers > CONTACTED_PEERS_THRESHOLD)
     || (el->totContactedRcvdPeers > CONTACTED_PEERS_THRESHOLD)) {
    /* Mail/DNS/HTTP server usually touch many hosts */
    if(!(isSMTPhost(el) || nameServerHost(el) || isHTTPhost(el))) {
      return(2);
    }
  }

  if((el->secHostPkts != NULL)
     && ((el->secHostPkts->nullPktsSent.value.value > 0)
	 || (el->secHostPkts->synFinPktsSent.value.value > 0)
	 || (el->secHostPkts->ackXmasFinSynNullScanSent.value.value > 0)
	 || (el->secHostPkts->tinyFragmentSent.value.value > 0)
	 || (el->secHostPkts->icmpFragmentSent.value.value > 0)
	 || (el->secHostPkts->overlappingFragmentSent.value.value > 0)
	 || (el->secHostPkts->malformedPktsRcvd.value.value > 0))) {
    return(2);
  }

  if((el->secHostPkts != NULL)
     && ((el->secHostPkts->rstPktsRcvd.value.value > 0)
	 || (el->secHostPkts->rejectedTCPConnRcvd.value.value > 0)
	 || (el->secHostPkts->udpToClosedPortRcvd.value.value > 0)
	 || (el->secHostPkts->udpToDiagnosticPortSent.value.value > 0)
	 || (el->secHostPkts->closedEmptyTCPConnSent.value.value > 0)
	 || (el->secHostPkts->icmpPortUnreachRcvd.value.value > 0)
	 || (el->secHostPkts->icmpHostNetUnreachRcvd.value.value > 0)
	 || (el->secHostPkts->icmpProtocolUnreachRcvd.value.value > 0)
	 || (el->secHostPkts->icmpAdminProhibitedRcvd.value.value > 0))) {
    return(1);
  }

  return(0);
}

/* ************************************ */

static void checkHostHealthness(HostTraffic *el) {
  char buf[LEN_GENERAL_WORK_BUFFER];

  if(hasWrongNetmask(el)
     || hasDuplicatedMac(el)
     || hasSentIpDataOnZeroPort(el)
     || (el->totContactedSentPeers > CONTACTED_PEERS_THRESHOLD) || (el->totContactedRcvdPeers > CONTACTED_PEERS_THRESHOLD)
     || ((el->secHostPkts != NULL)
	 && ((el->secHostPkts->nullPktsSent.value.value > 0)
	     || (el->secHostPkts->synFinPktsSent.value.value > 0)
	     || (el->secHostPkts->rstPktsRcvd.value.value > 0)
	     || (el->secHostPkts->ackXmasFinSynNullScanSent.value.value > 0)
	     || (el->secHostPkts->rejectedTCPConnRcvd.value.value > 0)
	     || (el->secHostPkts->udpToClosedPortRcvd.value.value > 0)
	     || (el->secHostPkts->udpToDiagnosticPortSent.value.value > 0)
	     || (el->secHostPkts->tinyFragmentSent.value.value > 0)
	     || (el->secHostPkts->icmpFragmentSent.value.value > 0)
	     || (el->secHostPkts->overlappingFragmentSent.value.value > 0)
	     || (el->secHostPkts->closedEmptyTCPConnSent.value.value > 0)
	     || (el->secHostPkts->icmpPortUnreachRcvd.value.value > 0)
	     || (el->secHostPkts->icmpHostNetUnreachRcvd.value.value > 0)
	     || (el->secHostPkts->icmpProtocolUnreachRcvd.value.value > 0)
	     || (el->secHostPkts->icmpAdminProhibitedRcvd.value.value > 0)
	     || (el->secHostPkts->malformedPktsRcvd.value.value > 0)))) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s "
		CONST_IMG_HIGH_RISK
		CONST_IMG_MEDIUM_RISK
                CONST_IMG_LOW_RISK
		"</TH><TD "TD_BG" ALIGN=RIGHT NOWRAP><OL>", getRowColor(),
		"Host Healthness (Risk Flags)");
    sendString(buf);

    if(hasDuplicatedMac(el))
      sendString("<LI>" CONST_IMG_HIGH_RISK "<A HREF=/" CONST_NTOP_HELP_HTML "#2>"
		 "Duplicated MAC found for this IP address (spoofing?)</A>\n");

    if(hasSentIpDataOnZeroPort(el))
      sendString("<LI>" CONST_IMG_HIGH_RISK "<A HREF=/" CONST_NTOP_HELP_HTML "#3>"
		 "Traffic on suspicious IP ports</A>\n");

    if(hasWrongNetmask(el))
      sendString("<LI>" CONST_IMG_MEDIUM_RISK "<A HREF=/" CONST_NTOP_HELP_HTML "#1>"
		 "Wrong network mask or bridging enabled</A>\n");

    if((el->totContactedSentPeers > CONTACTED_PEERS_THRESHOLD)
       || (el->totContactedRcvdPeers > CONTACTED_PEERS_THRESHOLD)) {
      sendString("<LI>" CONST_IMG_MEDIUM_RISK "<A HREF=/" CONST_NTOP_HELP_HTML "#4>"
		 "Suspicious activities: too many host contacts</A>\n");
    }

    if((el->secHostPkts != NULL)
       && ((el->secHostPkts->nullPktsSent.value.value > 0)
	   || (el->secHostPkts->synFinPktsSent.value.value > 0)
	   || (el->secHostPkts->ackXmasFinSynNullScanSent.value.value > 0)
	   || (el->secHostPkts->tinyFragmentSent.value.value > 0)
	   || (el->secHostPkts->icmpFragmentSent.value.value > 0)
	   || (el->secHostPkts->overlappingFragmentSent.value.value > 0)
	   || (el->secHostPkts->malformedPktsRcvd.value.value > 0))) {
      sendString("<LI>" CONST_IMG_MEDIUM_RISK "<A HREF=/" CONST_NTOP_HELP_HTML "#5>"
		 "Unexpected packets (e.g. traffic to closed port or connection reset)</A>:<br>\n");
      if(el->secHostPkts->synFinPktsSent.value.value > 0) sendString("[Sent: syn-fin]&nbsp;");
      if(el->secHostPkts->ackXmasFinSynNullScanSent.value.value > 0) sendString("[Sent: xmas]&nbsp;");
      if(el->secHostPkts->tinyFragmentSent.value.value > 0) sendString("[Sent: Tiny frag]&nbsp;");
      if(el->secHostPkts->icmpFragmentSent.value.value > 0) sendString("[Sent: icmp frag]&nbsp;");
      if(el->secHostPkts->overlappingFragmentSent.value.value > 0) sendString("[Sent: overlapfrag]&nbsp;");
      if(el->secHostPkts->malformedPktsRcvd.value.value > 0) sendString("[Rcvd: malformed]&nbsp;");
    }

    if((el->secHostPkts != NULL)
       && ((el->secHostPkts->rejectedTCPConnRcvd.value.value > 0)
	   || (el->secHostPkts->udpToClosedPortRcvd.value.value > 0)
	   || (el->secHostPkts->udpToDiagnosticPortSent.value.value > 0)
	   || (el->secHostPkts->rstPktsRcvd.value.value > 0)
	   || (el->secHostPkts->closedEmptyTCPConnSent.value.value > 0)
	   || (el->secHostPkts->icmpPortUnreachRcvd.value.value > 0)
	   || (el->secHostPkts->icmpHostNetUnreachRcvd.value.value > 0)
	   || (el->secHostPkts->icmpProtocolUnreachRcvd.value.value > 0)
	   || (el->secHostPkts->icmpAdminProhibitedRcvd.value.value > 0))) {
      sendString("<LI>" CONST_IMG_LOW_RISK "<A HREF=/" CONST_NTOP_HELP_HTML "#6>"
		 "Unexpected packets (e.g. traffic to closed port or connection reset)</A>:<br>\n");
      if(el->secHostPkts->rejectedTCPConnRcvd.value.value > 0) sendString("[Rcvd: rejected]&nbsp;");
      if(el->secHostPkts->udpToClosedPortRcvd.value.value > 0) sendString("[Sent: udp to closed]&nbsp;");
      if(el->secHostPkts->udpToDiagnosticPortSent.value.value > 0) sendString("[Sent: udp to diag]&nbsp;");
      if(el->secHostPkts->rstPktsRcvd.value.value > 0) sendString("[Rcvd: rst]&nbsp;");
      if(el->secHostPkts->closedEmptyTCPConnSent.value.value > 0) sendString("[Sent: closed-empty]&nbsp;");
      if(el->secHostPkts->icmpPortUnreachRcvd.value.value > 0) sendString("[Rcvd: port unreac]&nbsp;");
      if(el->secHostPkts->icmpHostNetUnreachRcvd.value.value > 0) sendString("[Rcvd: hostnet unreac]&nbsp;");
      if(el->secHostPkts->icmpProtocolUnreachRcvd.value.value > 0) sendString("[Rcvd: proto unreac]&nbsp;");
      if(el->secHostPkts->icmpAdminProhibitedRcvd.value.value > 0) sendString("[Rcvd: admin prohib]&nbsp;");
    }

    sendString("</OL></TD></TR>\n\n");
  }
}

/* ************************************ */

static void printUserList(HostTraffic *el) {
  char buf[LEN_GENERAL_WORK_BUFFER];
  UserList *list = el->protocolInfo->userList;
  int num = 0;

  while(list != NULL) {
    if(num > 0) sendString("<br>");

    if(FD_ISSET(BITFLAG_SMTP_USER, &(list->userFlags))) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<A HREF=\"mailto:%s\">%s</A>&nbsp;[&nbsp;SMTP&nbsp;]\n",
		    list->userName, list->userName); sendString(buf);
    } else {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s&nbsp;[", list->userName); sendString(buf);

      if(FD_ISSET(BITFLAG_POP_USER, &(list->userFlags))) sendString("&nbsp;POP&nbsp;");
      if(FD_ISSET(BITFLAG_IMAP_USER, &(list->userFlags))) sendString("&nbsp;IMAP&nbsp;");
      if(FD_ISSET(BITFLAG_SMTP_USER, &(list->userFlags))) sendString("&nbsp;SMTP&nbsp;");
      if(FD_ISSET(BITFLAG_P2P_USER, &(list->userFlags))) sendString("&nbsp;P2P&nbsp;");
      if(FD_ISSET(BITFLAG_FTP_USER, &(list->userFlags))) sendString("&nbsp;FTP&nbsp;");
      if(FD_ISSET(BITFLAG_MESSENGER_USER, &(list->userFlags))) sendString("&nbsp;MSG&nbsp;");
      if(FD_ISSET(BITFLAG_VOIP_USER, &(list->userFlags))) sendString("&nbsp;VoIP&nbsp;");
      if(FD_ISSET(BITFLAG_DAAP_USER, &(list->userFlags))) sendString("&nbsp;DAAP&nbsp;");
      sendString("]\n");
    }

    list = list->next;
    num++;
  }
}

/* ************************************ */

void checkHostProvidedServices(HostTraffic *el) {
  char buf[LEN_GENERAL_WORK_BUFFER];

  if(isServer(el)
     || isWorkstation(el)
     || isMasterBrowser(el)
     || isPrinter(el)
     || isBridgeHost(el)
     || nameServerHost(el) || isNtpServer(el)
     || gatewayHost(el) || isVoIPHost(el)
     || isSMTPhost(el) || isIMAPhost(el) || isPOPhost(el)
     || isDirectoryHost(el)
     || isFTPhost(el)
     || isHTTPhost(el)
     || isWINShost(el)
     || isDHCPClient(el) || isDHCPServer(el)
     || isFacebookClient(el) || isTwitterClient(el)
     ) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>", getRowColor(), "Host Type");
    sendString(buf);

    if(isServer(el))           sendString("Server<BR>\n");
    if(isWorkstation(el))      sendString("Workstation<BR>\n");
    if(isMasterBrowser(el))    sendString("Master Browser<BR>\n");

    if(isPrinter(el))          sendString("Printer&nbsp;" CONST_IMG_PRINTER "<BR>\n");
    if(isBridgeHost(el))       sendString("Layer-2 Switch/Bridge&nbsp;" CONST_IMG_BRIDGE "<BR>\n");

    if(nameServerHost(el))     sendString("Name Server&nbsp;" CONST_IMG_DNS_SERVER "<BR>\n");
    if(isNtpServer(el))        sendString("NTP Server&nbsp;" CONST_IMG_NTP_SERVER "<BR>\n");
    if(gatewayHost(el))        sendString("<A HREF="CONST_LOCAL_ROUTERS_LIST_HTML">Gateway/Router</A>&nbsp;" CONST_IMG_ROUTER "<BR>\n");

    if(isVoIPGateway(el) && (!isVoIPClient(el)))
      sendString("VoIP Gateway&nbsp;" CONST_IMG_VOIP_HOST "<BR>\n");
    else if(isVoIPClient(el))
      sendString("VoIP Host&nbsp;" CONST_IMG_VOIP_HOST "<BR>\n");

    if(isSMTPhost(el))         sendString("SMTP (Mail) Server&nbsp;" CONST_IMG_SMTP_SERVER "<BR>\n");
    if(isPOPhost(el))          sendString("POP Server&nbsp;" CONST_IMG_POP_SERVER "<BR>\n");
    if(isIMAPhost(el))         sendString("IMAP Server&nbsp;" CONST_IMG_IMAP_SERVER "<BR>\n");
    if(isDirectoryHost(el))    sendString("Directory Server&nbsp;" CONST_IMG_DIRECTORY_SERVER " <BR>\n");
    if(isFTPhost(el))          sendString("FTP Server&nbsp;" CONST_IMG_FTP_SERVER "<BR>\n");
    if(isHTTPhost(el))         sendString("HTTP Server&nbsp;" CONST_IMG_HTTP_SERVER "<BR>\n");
    if(isWINShost(el))         sendString("WINS Server<BR>\n");

    if(isDHCPClient(el))       sendString("BOOTP/DHCP Client&nbsp;" CONST_IMG_DHCP_CLIENT "<BR>\n");
    if(isDHCPServer(el))       sendString("BOOTP/DHCP Server&nbsp;" CONST_IMG_DHCP_SERVER "<BR>\n");

    if(isFacebookClient(el))   sendString(CONST_FACEBOOK_ICON" Client<BR>\n");
    if(isTwitterClient(el))    sendString(CONST_TWITTER_ICON" Client<BR>\n");

    sendString("</TD></TR>\n");
  }
}

/* ************************************ */

void printHostDetailedInfo(HostTraffic *el, int actualDeviceId) {
  char buf[4*LEN_GENERAL_WORK_BUFFER], buf1[64], osBuf[512];
  float percentage;
  Counter total;
  char *dynIp, *multihomed, *multivlaned;
  u_short as=0;
  HostTraffic *theHost;
  char formatBuf[LEN_TIMEFORMAT_BUFFER], formatBuf1[LEN_TIMEFORMAT_BUFFER],
    formatBuf2[32], custom_host_name[128];

  sendString("<CENTER>\n<P>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=80%%>\n");

  if(el->hostNumIpAddress[0] != '\0') {
    char *hostType;

    if(broadcastHost(el)) hostType = "broadcast";
    else if(multicastHost(el)) hostType = "multicast";
    else hostType = "unicast";

    if(isDHCPClient(el))
      dynIp = "/dynamic";
    else
      dynIp = "";

    if(isMultihomed(el) && (!broadcastHost(el)))
      multihomed = "&nbsp;-&nbsp;multihomed&nbsp;" CONST_IMG_MULTIHOMED;
    else
      multihomed = "";

    if(isMultivlaned(el) && (!broadcastHost(el)))
      multivlaned = "&nbsp;-&nbsp;multivlaned&nbsp;" CONST_IMG_MULTIVLANED;
    else
      multivlaned = "";

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s&nbsp;[%s%s%s%s]",
		  getRowColor(),
		  "IP&nbsp;Address", el->hostNumIpAddress,
		  hostType, dynIp, multihomed, multivlaned);
    sendString(buf);

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "&nbsp;[ <A title=\"Purge this host from ntop's memory\" class=tooltip "
		  "HREF=\"/"CONST_PURGE_HOST"?key=%u\">Purge Asset</A> <IMG SRC=/lock.png> ]",
		  el->serialHostIndex);
    sendString(buf);

    sendString("</TD></TR>\n\n");

#if 0
    if(isMultihomed(el) && (!broadcastHost(el))) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		    "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD ALIGN=RIGHT>&nbsp;<OL>",
		    getRowColor(), "Multihomed Addresses");
      sendString(buf);

      for(theHost = getFirstHost(actualDeviceId);
	  theHost != NULL; theHost = getNextHost(actualDeviceId, theHost)) {
	if((theHost != el)
	   && (memcmp(theHost->ethAddress, el->ethAddress, LEN_ETHERNET_ADDRESS) == 0)) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<LI><A HREF=/%s.html>%s</A>",
			theHost->hostNumIpAddress, theHost->hostNumIpAddress);
	  sendString(buf);
	}
      } /* for */

      sendString("</TD></TR>\n");
    }
#endif

    if((el->network_mask > 0) && (el->network_mask != 32)) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		    "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		    "<TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n\n", getRowColor(),
		    "Host&nbsp;Network", host2networkName(el, buf1, sizeof(buf1)));
      sendString(buf);
    }

    if((el->protocolInfo != NULL) && (el->protocolInfo->dhcpStats != NULL)) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>",
		  getRowColor(), "DHCP Information");
      sendString(buf);

      sendString("<TD "TD_BG"><TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=100%>\n");

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT COLSPAN=2>%s</TD></TR>\n\n", getRowColor(), "DHCP Server",
		  _intoa(el->protocolInfo->dhcpStats->dhcpServerIpAddress, buf1, sizeof(buf1)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT COLSPAN=2>%s</TD></TR>\n\n", getRowColor(), "Previous IP Address",
		  _intoa(el->protocolInfo->dhcpStats->previousIpAddress, buf1, sizeof(buf1)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT COLSPAN=2>%s</TD></TR>\n\n",
		  getRowColor(), "Address Assigned on",
		  formatTime(&(el->protocolInfo->dhcpStats->assignTime), formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT COLSPAN=2>%s</TD></TR>\n\n",
		  getRowColor(), "To be Renewed Before",
		  formatTime(&(el->protocolInfo->dhcpStats->renewalTime), formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT COLSPAN=2>%s</TD></TR>\n\n",
		  getRowColor(), "Lease Ends on",
		  formatTime(&(el->protocolInfo->dhcpStats->leaseTime), formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">DHCP Packets</TH>"
		  "<TH "TH_BG" ALIGN=CENTER "DARK_BG">Sent</TH><TH "TH_BG" ALIGN=RIGHT "DARK_BG">Rcvd</TH></TR>\n\n",
		  getRowColor());
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n\n",
		  getRowColor(), "DHCP Discover",
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgSent[FLAG_DHCP_DISCOVER_MSG].value, formatBuf, sizeof(formatBuf)),
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgRcvd[FLAG_DHCP_DISCOVER_MSG].value, formatBuf1, sizeof(formatBuf1)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n\n",
		  getRowColor(), "DHCP Offer",
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgSent[FLAG_DHCP_OFFER_MSG].value, formatBuf, sizeof(formatBuf)),
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgRcvd[FLAG_DHCP_OFFER_MSG].value, formatBuf1, sizeof(formatBuf1)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n\n",
		  getRowColor(), "DHCP Request",
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgSent[FLAG_DHCP_REQUEST_MSG].value, formatBuf, sizeof(formatBuf)),
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgRcvd[FLAG_DHCP_REQUEST_MSG].value, formatBuf1, sizeof(formatBuf1)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n\n",
		  getRowColor(), "DHCP Decline",
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgSent[FLAG_DHCP_DECLINE_MSG].value, formatBuf, sizeof(formatBuf)),
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgRcvd[FLAG_DHCP_DECLINE_MSG].value, formatBuf1, sizeof(formatBuf1)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n\n",
		  getRowColor(), "DHCP Ack",
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgSent[FLAG_DHCP_ACK_MSG].value, formatBuf, sizeof(formatBuf)),
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgRcvd[FLAG_DHCP_ACK_MSG].value, formatBuf1, sizeof(formatBuf1)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n\n",
		  getRowColor(), "DHCP Nack",
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgSent[FLAG_DHCP_NACK_MSG].value, formatBuf, sizeof(formatBuf)),
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgRcvd[FLAG_DHCP_NACK_MSG].value, formatBuf1, sizeof(formatBuf1)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n\n",
		  getRowColor(), "DHCP Release",
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgSent[FLAG_DHCP_RELEASE_MSG].value, formatBuf, sizeof(formatBuf)),
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgRcvd[FLAG_DHCP_RELEASE_MSG].value, formatBuf1, sizeof(formatBuf1)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n\n",
		  getRowColor(), "DHCP Inform",
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgSent[FLAG_DHCP_INFORM_MSG].value, formatBuf, sizeof(formatBuf)),
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgRcvd[FLAG_DHCP_INFORM_MSG].value, formatBuf1, sizeof(formatBuf1)));
      sendString(buf);


      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n\n",
		  getRowColor(), "DHCP Unknown Msg",
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgSent[FLAG_DHCP_UNKNOWN_MSG].value,
			     formatBuf, sizeof(formatBuf)),
		  formatPkts(el->protocolInfo->dhcpStats->dhcpMsgRcvd[FLAG_DHCP_UNKNOWN_MSG].value,
			     formatBuf1, sizeof(formatBuf1)));
      sendString(buf);

      sendString("</TABLE>"TABLE_OFF"</TD></TR>\n\n");
    }
  } else {
    if(el->l2Host) {
      u_int8_t header_sent = 0;

      for(theHost = getFirstHost(actualDeviceId); theHost != NULL; theHost = getNextHost(actualDeviceId, theHost)) {
	if((theHost != el)
	   && (!theHost->l2Host)
	   && (memcmp(theHost->lastEthAddress, el->ethAddress, LEN_ETHERNET_ADDRESS) == 0)) {

	  if(!header_sent) {
	    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
			  "<div style=\"height:120px;width:400px;overflow-x:hidden;overflow-y:auto;\">",
			  getRowColor(), "IP Addresses Bound to this MAC");

	    sendString(buf);
	    header_sent = 1;
	  }

	  sendString(makeHostLink(theHost, 0, 0, 0, buf, sizeof(buf)));
	  sendString("</br>\n");
	}
      } /* for */

      if(header_sent)
	sendString("</DIV></TD></TR>\n");
    }
  }

  /* Read custom host name if any */
  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "hostname.%s",
		(el->hostNumIpAddress[0] != '\0') ? el->hostNumIpAddress : el->ethAddressString);

  if(fetchPrefsValue(buf, custom_host_name, sizeof(custom_host_name)) == -1)
    custom_host_name[0] = '\0';

  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		"<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">Custom Host Name</TH>"
		"<TD "TD_BG" ALIGN=RIGHT>"
		"<FORM METHOD=GET ACTION=/editPrefs.html>"
		"<INPUT TYPE=hidden name=key value=\"hostname.%s\">"
		"<input type=text name=val value=\"%s\">"
		"<INPUT TYPE=image src=/"CONST_EDIT_IMG" value=\"Submit\" alt=\"Set custom host name\"></TD></TR>\n\n",
		getRowColor(),
		(el->hostNumIpAddress[0] != '\0') ? el->hostNumIpAddress : el->ethAddressString,
		custom_host_name);
  sendString(buf);

  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH>"
	      "<TD "TD_BG" ALIGN=RIGHT>"
	      "%s&nbsp;&nbsp;-&nbsp;&nbsp;%s&nbsp;[Inactive since %s]</TD></TR>\n\n",
	      getRowColor(),
	      "First/Last&nbsp;Seen",
	      formatTime(&(el->firstSeen), formatBuf, sizeof(formatBuf)),
	      formatTime(&(el->lastSeen), formatBuf1, sizeof(formatBuf1)),
	      formatSeconds(myGlobals.actTime-el->lastSeen, formatBuf2, sizeof(formatBuf2)));
  sendString(buf);


  if(el->hostAS != 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "<A class=external HREF=\"" DEFAULT_AS_LOOKUP_URL "\">%d</A>"
		  "&nbsp;[%s]</TD></TR>\n\n",
		  getRowColor(), "Autonomous System",
		  el->hostAS, el->hostAS, (el->hostASDescr != NULL) ? el->hostASDescr : "No AS info");
    sendString(buf);
  }

  if((el->known_subnet_id != UNKNOWN_SUBNET_ID)
     && (el->known_subnet_id < myGlobals.numKnownSubnets)) {
    char subnet_buf[48];

    host2networkName(el, subnet_buf, sizeof(subnet_buf));

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "<A HREF=\""CONST_DOMAIN_STATS_HTML"?dom=%d&netmode=1\">%s</A></TD></TR>\n\n", getRowColor(),
		  "Subnet", el->known_subnet_id, subnet_buf);
    sendString(buf);
  }

  if(el->dnsDomainValue && (el->dnsDomainValue[0] != '\0')) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "%s</TD></TR>\n\n", getRowColor(),
		  "Domain", el->dnsDomainValue);
    sendString(buf);
  }

  if((el->ethAddressString[0] != '\0')
     && strcmp(el->ethAddressString, "00:00:00:00:00:00")
     && strcmp(el->ethAddressString, "00:01:02:03:04:05") /* dummy address */) {

    if(isMultihomed(el)) {
      char *symMacAddr, symLink[32];
      char etherbuf[LEN_ETHERNET_ADDRESS_DISPLAY];

      symMacAddr = etheraddr_string(el->ethAddress, etherbuf);
      strcpy(symLink, symMacAddr);
      urlFixupToRFC1945Inplace(symLink);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "<A HREF=%s.html>%s</A>%s</TD></TR>\n\n",
		  getRowColor(), "Main Host MAC Address",
		  symLink, symMacAddr,
		  myGlobals.separator /* it avoids empty cells not to be rendered */);
      sendString(buf);

    } else {
      char *vendorName = getVendorInfo(el->ethAddress, 1), macbuf[32];

      safe_snprintf(__FILE__, __LINE__, macbuf, sizeof(macbuf), "%s", el->ethAddressString);
      macbuf[2] = macbuf[5] = macbuf[8] = macbuf[11] = macbuf[14] = '_';

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		    "<A HREF=%s.html>%s</A> %s%s%s%s</TD></TR>\n\n",
		    getRowColor(), "MAC&nbsp;Address <IMG ALT=\"Network Interface Card (NIC)\" SRC=/card.gif BORDER=0 "TABLE_DEFAULTS">",
		    macbuf, el->ethAddressString,
		    myGlobals.separator /* it avoids empty cells not to be rendered */,
		    (vendorName[0] != '\0') ? "[" : "",
		    vendorName,
		    (vendorName[0] != '\0') ? "]" : "");
      sendString(buf);
    }
  }

  if(((el->lastEthAddress[0] != 0)
      || (el->lastEthAddress[1] != 0)
      || (el->lastEthAddress[2] != 0)
      || (el->lastEthAddress[3] != 0)
      || (el->lastEthAddress[4] != 0)
      || (el->lastEthAddress[5] != 0) /* The address isn't empty */)
     && (memcmp(el->lastEthAddress, el->ethAddress, LEN_ETHERNET_ADDRESS) != 0)) {
    /* Different MAC addresses */
    char *symMacAddr, symLink[32], shortBuf[64];
    char etherbuf[LEN_ETHERNET_ADDRESS_DISPLAY];
    char *vendorName;

    symMacAddr = etheraddr_string(el->lastEthAddress, etherbuf);
    strcpy(symLink, symMacAddr);
    urlFixupToRFC1945Inplace(symLink);

    safe_snprintf(__FILE__, __LINE__, shortBuf, sizeof(shortBuf), "<A HREF=%s.html>%s</A>", symLink, symMacAddr);

    vendorName = getVendorInfo(el->lastEthAddress, 1);

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "%s %s%s%s%s"
		  "</TD></TR>\n\n",
		  getRowColor(),
		  "Last MAC Address/Router <IMG ALT=\"Network Interface Card (NIC)/Router\" SRC=/card.gif BORDER=0 "TABLE_DEFAULTS">",
		  shortBuf,
		  myGlobals.separator /* it avoids empty cells not to be rendered */,
		  (vendorName[0] != '\0') ? "[" : "",
		  vendorName,
		  (vendorName[0] != '\0') ? "]" : "");
    sendString(buf);
  }

  if(el->hostNumIpAddress[0] != '\0') {
    setHostFingerprint(el);

    if((el->fingerprint != NULL) && (el->fingerprint[0] == ':') && (strlen(el->fingerprint) > 2)) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "%s%s</TD></TR>\n\n",
		  getRowColor(), "OS&nbsp;Name",
		  getOSFlag(el, NULL, 1, osBuf, sizeof(osBuf)),
		  myGlobals.separator /* it avoids empty cells not to be rendered */);
      sendString(buf);
    }

    {
      char linkName[256], vlanStr[32];

      if(el->hostNumIpAddress[0] != '\0') {
	strncpy(linkName, el->hostNumIpAddress, sizeof(linkName));
      } else {
	strncpy(linkName, el->ethAddressString, sizeof(linkName));
      }
    
      /* For Ethernet and IPv6 addresses */
      urlFixupToRFC1945Inplace(linkName);
    
      if(el->vlanId > 0) {
	safe_snprintf(__FILE__, __LINE__, vlanStr, sizeof(vlanStr), "-%d", el->vlanId);
      } else
	vlanStr[0] = '\0';

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT><A href=\"#\" onclick=\"window.open(\'/%s-%s%s%s',"
		    " \'Contacts Map\', \'height=530, width=750,toolbar=nodirectories=no,status=no,menubar=no,scrollbars=no,resizable=no\'); return false;\">Map <IMG SRC=\"/Google_Maps_Marker.png\" BORDER=0></A>",
		    getRowColor(), "Contacts&nbsp;Map",
		    CONST_HOST_IP_MAP_HTML, linkName, vlanStr, CHART_FORMAT);
      sendString(buf);
    }

    sendString("</TD></TR>\n\n");
  }

  if((as = getHostAS(el)) != 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "<A HREF=/%s?dom=%d&netmode=2>%d</A></TD></TR>\n\n", getRowColor(), "Origin&nbsp;AS", CONST_DOMAIN_STATS_HTML, as, as);
    sendString(buf);
  }

  if(el->vlanId != NO_VLAN) {
    char tmpBuf[64];

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "%s</TD></TR>\n\n", getRowColor(), "VLAN&nbsp;Id", vlan2name(el->vlanId, tmpBuf, sizeof(tmpBuf)));
    sendString(buf);
  }

  if(el->ifId != NO_INTERFACE) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "%d</TD></TR>\n\n", getRowColor(), "Interface&nbsp;Id", el->ifId);
    sendString(buf);
  }

  if(el->nonIPTraffic) {
    if((el->nonIPTraffic->nbHostName != NULL) || (el->nonIPTraffic->nbDomainName != NULL)) {
      if(el->nonIPTraffic->nbAccountName) {
	if(el->nonIPTraffic->nbDomainName != NULL) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
			"%s@%s&nbsp;[domain %s] (%s) %s</TD></TR>\n\n",
			getRowColor(), "NetBios&nbsp;Name",
			el->nonIPTraffic->nbAccountName, el->nonIPTraffic->nbHostName, el->nonIPTraffic->nbDomainName,
			getNbNodeType(el->nonIPTraffic->nbNodeType),
			el->nonIPTraffic->nbDescr ? el->nonIPTraffic->nbDescr : "");
	} else {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		      "%s@%s (%s) %s</TD></TR>\n\n",
		      getRowColor(), "NetBios&nbsp;Name",
		      el->nonIPTraffic->nbAccountName, el->nonIPTraffic->nbHostName,
		      getNbNodeType(el->nonIPTraffic->nbNodeType),
		      el->nonIPTraffic->nbDescr ? el->nonIPTraffic->nbDescr : "");
	}
      } else {
	if(el->nonIPTraffic->nbDomainName != NULL) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		      "%s&nbsp;[domain %s] (%s) %s</TD></TR>\n\n",
		      getRowColor(), "NetBios&nbsp;Name",
		      el->nonIPTraffic->nbHostName != NULL ? el->nonIPTraffic->nbHostName : "",
		      el->nonIPTraffic->nbDomainName,
		      getNbNodeType(el->nonIPTraffic->nbNodeType),
		      el->nonIPTraffic->nbDescr ? el->nonIPTraffic->nbDescr : "");
	} else {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		      "%s (%s) %s</TD></TR>\n\n",
		      getRowColor(), "NetBios&nbsp;Name",
		      el->nonIPTraffic->nbHostName,
		      getNbNodeType(el->nonIPTraffic->nbNodeType),
		      el->nonIPTraffic->nbDescr ? el->nonIPTraffic->nbDescr : "");
	}
      }

      sendString(buf);
    } else if(el->nonIPTraffic->nbHostName != NULL) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "%s&nbsp;(%s) %s</TD></TR>\n\n",
		  getRowColor(), "NetBios&nbsp;Name",
		  el->nonIPTraffic->nbHostName, getNbNodeType(el->nonIPTraffic->nbNodeType),
		  el->nonIPTraffic->nbDescr ? el->nonIPTraffic->nbDescr : "");
      sendString(buf);
    }
  }

  if(!multicastHost(el)) {
    if(subnetPseudoLocalHost(el)) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		    "%s</TD></TR>\n\n", getRowColor(),
		    "Host&nbsp;Location",
		    "Local (inside specified/local subnet or known network list)");
    } else {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		    "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		    "%s</TD></TR>\n\n", getRowColor(),
		    "Host&nbsp;Location",
		    "Remote (outside specified/local subnet)");
    }
    sendString(buf);
  }


  if(el->geo_ip) {
    char *countryIcon, buf3[512] = { '\0' };

    if(myGlobals.runningPref.mapperURL) buildMapLink(el, buf3, sizeof(buf3));
    countryIcon = getHostCountryIconURL(el);

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "%s, %s %s&nbsp;%s&nbsp;</TD></TR>\n\n", getRowColor(), "Physical Location",
		  el->geo_ip->city ? el->geo_ip->city : "Unknown city", el->geo_ip->country_name, countryIcon, buf3);
    sendString(buf);
  }

  if(el->community) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "<A HREF=/"CONST_COMMUNITIES_STATS_HTML"?community=%s>%s</A>"
		  "</TD></TR>\n\n", getRowColor(), "Community",
		  el->community, el->community);
    sendString(buf);
  }

  if(el->hwModel) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "%s</TD></TR>\n\n", getRowColor(), "Hardware Model", el->hwModel);
    sendString(buf);
  }

  if(el->description) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "%s</TD></TR>\n\n", getRowColor(), "Description", el->description);
    sendString(buf);
  }

  if(el->minTTL > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		"%d:%d&nbsp;[~%d&nbsp;hop(s)]</TD></TR>\n\n",
		getRowColor(), "IP&nbsp;TTL&nbsp;(Time to Live)",
		el->minTTL, el->maxTTL, guessHops(el));
    sendString(buf);
  }

  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s"
	      "</TH><TD "TD_BG" ALIGN=RIGHT>"
	      "%s/%s Pkts/%s Retran. Pkts [%d%%]</TD></TR>\n\n",
	      getRowColor(), "Total&nbsp;Data&nbsp;Sent",
	      formatBytes(el->bytesSent.value, 1, formatBuf, sizeof(formatBuf)),
	      formatPkts(el->pktsSent.value, formatBuf1, sizeof(formatBuf1)),
	      formatPkts(el->pktsDuplicatedAckSent.value, formatBuf2, sizeof(formatBuf2)),
	      (int)(((float)el->pktsDuplicatedAckSent.value*100)/(float)(el->pktsSent.value+1))
	      );
  sendString(buf);

  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		"<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
	      "%s Pkts</TD></TR>\n\n",
	      getRowColor(), "Broadcast&nbsp;Pkts&nbsp;Sent",
	      formatPkts(el->pktsBroadcastSent.value, formatBuf, sizeof(formatBuf)));
  sendString(buf);

  if(el->routedTraffic != NULL) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR  "TR_ON"%s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		"%s/%s Pkts</TD></TR>\n\n",
		getRowColor(), "Routed Traffic",
		formatBytes(el->routedTraffic->routedBytes.value, 1, formatBuf, sizeof(formatBuf)),
		formatPkts(el->routedTraffic->routedPkts.value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);
  }

  if((el->pktsMulticastSent.value > 0) || (el->pktsMulticastRcvd.value > 0)) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s</TH><TD "TD_BG" ALIGN=RIGHT>",
		getRowColor(), "Multicast&nbsp;Traffic");
    sendString(buf);

    if(el->pktsMulticastSent.value > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Sent&nbsp;%s/%s&nbsp;Pkts&nbsp;",
		  formatBytes(el->bytesMulticastSent.value, 1, formatBuf, sizeof(formatBuf)),
		  formatPkts(el->pktsMulticastSent.value, formatBuf1, sizeof(formatBuf1)));
      sendString(buf);
    }

    if(el->pktsMulticastRcvd.value > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%sRcvd&nbsp;%s/%s&nbsp;Pkts",
		    (el->pktsMulticastSent.value > 0) ? " - " : "",
		    formatBytes(el->bytesMulticastRcvd.value, 1, formatBuf, sizeof(formatBuf)),
		    formatPkts(el->pktsMulticastRcvd.value, formatBuf1, sizeof(formatBuf1)));
      sendString(buf);
    }

    sendString("</TD></TR>\n\n");
  }

  if(el->bytesSent.value == 0)
    percentage = 0;
  else
    percentage = 100 - (((float)el->bytesSentRem.value*100)/el->bytesSent.value);

  if(el->hostNumIpAddress[0] != '\0') {
    printTableEntryPercentage(buf, sizeof(buf), "Data&nbsp;Sent&nbsp;Stats",
			      "Local", "Rem", -1, percentage, 0, 0);

    if(el->bytesSent.value > 0) {
      percentage = (((float)el->ipv4BytesSent.value*100)/el->bytesSent.value);
      printTableEntryPercentage(buf, sizeof(buf), "IP&nbsp;vs.&nbsp;Non-IP&nbsp;Sent",
				"IP", "Non-IP", -1, percentage, 0, 0);
    }
  }

  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%s"
	      "</TH><TD "TD_BG" ALIGN=RIGHT>"
	      "%s/%s Pkts/%s Retran. Pkts [%d%%]</TD></TR>\n\n",
	      getRowColor(), "Total&nbsp;Data&nbsp;Rcvd",
	      formatBytes(el->bytesRcvd.value, 1, formatBuf, sizeof(formatBuf)),
	      formatPkts(el->pktsRcvd.value, formatBuf1, sizeof(formatBuf1)),
	      formatPkts(el->pktsDuplicatedAckRcvd.value, formatBuf2, sizeof(formatBuf2)),
	      (int)((float)(el->pktsDuplicatedAckRcvd.value*100)/(float)(el->pktsRcvd.value+1)));
  sendString(buf);

  if(el->bytesRcvd.value == 0)
    percentage = 0;
  else
    percentage = 100 - (((float)el->bytesRcvdFromRem.value*100)/el->bytesRcvd.value);

    if(el->hostNumIpAddress[0] != '\0')
      printTableEntryPercentage(buf, sizeof(buf), "Data&nbsp;Rcvd&nbsp;Stats",
				"Local", "Rem", -1, percentage, 0, 0);

  if(el->bytesRcvd.value > 0) {
    percentage = (((float)el->ipv4BytesRcvd.value*100)/el->bytesRcvd.value);
    printTableEntryPercentage(buf, sizeof(buf), "IP&nbsp;vs.&nbsp;Non-IP&nbsp;Rcvd",
			      "IP", "Non-IP", -1, percentage, 0, 0);
  }

  total = el->pktsSent.value+el->pktsRcvd.value;
  if(total > 0) {
    percentage = ((float)el->pktsSent.value*100)/((float)total);
    printTableEntryPercentage(buf, sizeof(buf), "Sent&nbsp;vs.&nbsp;Rcvd&nbsp;Pkts",
			      "Sent", "Rcvd", -1, percentage, 0, 0);
  }

  total = el->bytesSent.value+el->bytesRcvd.value;
  if(total > 0) {
    percentage = ((float)el->bytesSent.value*100)/((float)total);
    printTableEntryPercentage(buf, sizeof(buf), "Sent&nbsp;vs.&nbsp;Rcvd&nbsp;Data",
			      "Sent", "Rcvd", -1, percentage, 0, 0);
  }

  /* ******************** */

  checkHostProvidedServices(el);

  /* **************************** */

  if((el->protocolInfo) && (el->protocolInfo->userList != NULL)) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">"
		"Known&nbsp;Users&nbsp;" CONST_IMG_HAS_USERS "</TH><TD "TD_BG" ALIGN=RIGHT>\n",
		getRowColor());
    sendString(buf);

    printUserList(el);
    sendString("<br>\n</TD></TR>\n\n");
  }

  /* **************************** */

  if((el->hostNumIpAddress[0] != '\0')
     && (!subnetPseudoLocalHost(el))
     && (!multicastHost(el))
     && (!privateIPAddress(el))) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">"
		  "%s</TH><TD "TD_BG" ALIGN=RIGHT>"
		  "[ <A class=external HREF=\""DEFAULT_WHOIS_LOOKUP_URL"\">Whois</A> ]\n",
		  getRowColor(), "Further Host Information", el->hostNumIpAddress);
    sendString(buf);

    sendString("</TD></TR>\n\n");
  }

  /* RRD */
  if(el->hostNumIpAddress[0] != '\0') {
    char rrdBuf[LEN_GENERAL_WORK_BUFFER];

    hostRRdGraphLink(el, NETWORK_VIEW, 0, rrdBuf, sizeof(rrdBuf));

    if(rrdBuf[0] != '\0') {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                  "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">Historical Data</TH>\n"
		    "<TD "TD_BG" ALIGN=\"right\">%s</TD></TR>\n\n",
		    getRowColor(), rrdBuf);
      sendString(buf);
    }
  }

  checkHostHealthness(el);

  sendString("</TABLE>"TABLE_OFF"<P>\n");
  sendString("</CENTER>\n");
}

/* ************************************ */

void printServiceStats(char* svcName, ServiceStats* ss,
		       short printSentStats) {
  char buf[LEN_GENERAL_WORK_BUFFER];
  Counter tot, tot1;
  float f1, f2, f3, f4;
  char formatBuf[32], formatBuf1[32], formatBuf2[32], formatBuf3[32], formatBuf4[32],
    formatBuf5[32], formatBuf6[32], formatBuf7[32];

  if(ss != NULL) {
    if(printSentStats) {
      tot = ss->numLocalReqSent.value+ss->numRemReqSent.value;

      if(tot == 0)
	f1 = f2 = 0;
      else {
	f1 = (ss->numLocalReqSent.value*100)/tot;
	f2 = (ss->numRemReqSent.value*100)/tot;
      }

      tot1 = ss->numPositiveReplRcvd.value+ss->numNegativeReplRcvd.value;
      if(tot1 == 0)
	f3 = f4 = 0;
      else {
	f3 = (ss->numPositiveReplRcvd.value*100)/tot1;
	f4 = (ss->numNegativeReplRcvd.value*100)/tot1;
      }

      if((tot > 0) || (tot1 > 0)) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" "DARK_BG">%s</TH>"
		    "<TD "TD_BG" ALIGN=CENTER>%s</TD><TD "TD_BG" ALIGN=CENTER>%.1f%%</TD>"
		    "<TD "TD_BG" ALIGN=CENTER>%s</TD><TD "TD_BG" ALIGN=CENTER>%.1f%%</TD>"
		    "<TD "TD_BG" ALIGN=CENTER>%s</TD><TD "TD_BG" ALIGN=CENTER>%.1f%%</TD>"
		    "<TD "TD_BG" ALIGN=CENTER>%s</TD><TD "TD_BG" ALIGN=CENTER>%.1f%%</TD>"
		    "<TD "TD_BG" ALIGN=CENTER>%s - %s</TD><TD "TD_BG" ALIGN=CENTER>%s - %s</TD>"
		    "</TR>\n\n",
		    getRowColor(), svcName,
		    formatPkts(ss->numLocalReqSent.value, formatBuf, sizeof(formatBuf)), f1,
		    formatPkts(ss->numRemReqSent.value, formatBuf1, sizeof(formatBuf1)), f2,
		    formatPkts(ss->numPositiveReplRcvd.value, formatBuf2, sizeof(formatBuf2)), f3,
		    formatPkts(ss->numNegativeReplRcvd.value, formatBuf3, sizeof(formatBuf3)), f4,
		    formatMicroSeconds(ss->fastestMicrosecLocalReqMade, formatBuf4, sizeof(formatBuf4)),
		    formatMicroSeconds(ss->slowestMicrosecLocalReqMade, formatBuf5, sizeof(formatBuf5)),
		    formatMicroSeconds(ss->fastestMicrosecRemReqMade, formatBuf6, sizeof(formatBuf6)),
		    formatMicroSeconds(ss->slowestMicrosecRemReqMade, formatBuf7, sizeof(formatBuf7))
		    );
	sendString(buf);
      }
    } else {
      tot = ss->numLocalReqRcvd.value+ss->numRemReqRcvd.value;

      if(tot == 0)
	f1 = f2 = 0;
      else {
	f1 = (ss->numLocalReqRcvd.value*100)/tot;
	f2 = (ss->numRemReqRcvd.value*100)/tot;
      }

      tot1 = ss->numPositiveReplSent.value+ss->numNegativeReplSent.value;
      if(tot1 == 0)
	f3 = f4 = 0;
      else {
	f3 = (ss->numPositiveReplSent.value*100)/tot1;
	f4 = (ss->numNegativeReplSent.value*100)/tot1;
      }

      if((tot > 0) || (tot1 > 0)) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG">%s</TH>"
                "<TD "TD_BG" ALIGN=CENTER>%s</TD><TD "TD_BG" ALIGN=CENTER>%.1f%%</TD>"
		"<TD "TD_BG" ALIGN=CENTER>%s</TD><TD "TD_BG" ALIGN=CENTER>%.1f%%</TD>"
		"<TD "TD_BG" ALIGN=CENTER>%s</TD><TD "TD_BG" ALIGN=CENTER>%.1f%%</TD>"
		"<TD "TD_BG" ALIGN=CENTER>%s</TD><TD "TD_BG" ALIGN=CENTER>%.1f%%</TD>"
		"<TD "TD_BG" ALIGN=CENTER>%s - %s</TD><TD "TD_BG" ALIGN=CENTER>%s - %s</TD>"
		"</TR>\n\n",
		getRowColor(), svcName,
		formatPkts(ss->numLocalReqRcvd.value, formatBuf, sizeof(formatBuf)), f1,
		formatPkts(ss->numRemReqRcvd.value, formatBuf1, sizeof(formatBuf1)), f2,
		formatPkts(ss->numPositiveReplSent.value, formatBuf2, sizeof(formatBuf2)), f3,
		formatPkts(ss->numNegativeReplSent.value, formatBuf3, sizeof(formatBuf3)), f4,
		formatMicroSeconds(ss->fastestMicrosecLocalReqServed, formatBuf4, sizeof(formatBuf4)),
		formatMicroSeconds(ss->slowestMicrosecLocalReqServed, formatBuf5, sizeof(formatBuf5)),
		formatMicroSeconds(ss->fastestMicrosecRemReqServed, formatBuf6, sizeof(formatBuf6)),
		formatMicroSeconds(ss->slowestMicrosecRemReqServed, formatBuf7, sizeof(formatBuf7))
		);
	sendString(buf);
      }
    }
  }
}

/* ************************************ */

void printHostUsedServices(HostTraffic *el, int actualDeviceId) {
  Counter tot;

  if((el->protocolInfo == NULL)
     || ((el->protocolInfo->dnsStats == NULL)
	 && (el->protocolInfo->httpStats == NULL)))
    return;

  tot = 0;

  if(el->protocolInfo->dnsStats)
    tot += el->protocolInfo->dnsStats->numLocalReqSent.value + el->protocolInfo->dnsStats->numRemReqSent.value;

  if(el->protocolInfo->httpStats)
    tot += el->protocolInfo->httpStats->numLocalReqSent.value + el->protocolInfo->httpStats->numRemReqSent.value;

  if(tot > 0) {
    printSectionTitle("Client&nbsp;Role");
    sendString("<CENTER>\n");
    sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=100%>\n<TR "TR_ON" "DARK_BG">"
	       "<TH "TH_BG">&nbsp;</TH>"
	       "<TH "TH_BG" COLSPAN=2>#&nbsp;Loc.&nbsp;Req.&nbsp;Sent</TH>"
	       "<TH "TH_BG" COLSPAN=2>#&nbsp;Rem.&nbsp;Req.&nbsp;Sent</TH>"
	       "<TH "TH_BG" COLSPAN=2>#&nbsp;Pos.&nbsp;Reply&nbsp;Rcvd</TH>"
	       "<TH "TH_BG" COLSPAN=2>#&nbsp;Neg.&nbsp;Reply&nbsp;Rcvd</TH>"
	       "<TH "TH_BG">Local&nbsp;RndTrip</TH>"
	       "<TH "TH_BG">Rem&nbsp;RndTrip</TH>"
	       "</TR>\n\n");

    if(el->protocolInfo->dnsStats)  printServiceStats("DNS", el->protocolInfo->dnsStats, 1);
    if(el->protocolInfo->httpStats) printServiceStats("HTTP", el->protocolInfo->httpStats, 1);

    sendString("</TABLE>"TABLE_OFF"\n");
    sendString("</CENTER>\n");
  }

  /* ************ */

  tot = 0;

  if(el->protocolInfo->dnsStats)
    tot += el->protocolInfo->dnsStats->numLocalReqRcvd.value+el->protocolInfo->dnsStats->numRemReqRcvd.value;

  if(el->protocolInfo->httpStats)
    tot += el->protocolInfo->httpStats->numLocalReqRcvd.value+el->protocolInfo->httpStats->numRemReqRcvd.value;

  if(tot > 0) {
    printSectionTitle("Server&nbsp;Role");
    sendString("<CENTER>\n");
    sendString("<P>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=100%>\n<TR "TR_ON" "DARK_BG">"
	       "<TH "TH_BG">&nbsp;</TH>"
	       "<TH "TH_BG" COLSPAN=2>#&nbsp;Loc.&nbsp;Req.&nbsp;Rcvd</TH>"
	       "<TH "TH_BG" COLSPAN=2>#&nbsp;Rem.&nbsp;Req.&nbsp;Rcvd</TH>"
	       "<TH "TH_BG" COLSPAN=2>#&nbsp;Pos.&nbsp;Reply&nbsp;Sent</TH>"
	       "<TH "TH_BG" COLSPAN=2>#&nbsp;Neg.&nbsp;Reply&nbsp;Sent</TH>"
	       "<TH "TH_BG">Local&nbsp;RndTrip</TH>"
	       "<TH "TH_BG">Rem&nbsp;RndTrip</TH>"
	       "</TR>\n\n");

    if(el->protocolInfo->dnsStats) printServiceStats("DNS", el->protocolInfo->dnsStats, 0);
    if(el->protocolInfo->httpStats) printServiceStats("HTTP", el->protocolInfo->httpStats, 0);

    sendString("</TABLE>"TABLE_OFF"\n");
    sendString("</CENTER>\n");
  }
}

/* ********************************** */

void printTableEntry(char *buf, int bufLen,
		     char *label, char* color,
		     float total, float percentage,
		     u_int showFlows, Counter flows,
		     u_int showRRD) {
  int int_perc;
  char formatBuf[32], flowBuf[64], tmpBuf[32], rrdBuf[768];
  struct stat statbuf;
  char _label[256];

  encodeString(label, _label, sizeof(_label));

  /* traceEvent(CONST_TRACE_INFO,  "'%s' -> '%s'", label, _label); */

  if(total == 0) return;

  int_perc = (int)percentage;

  /* This shouldn't happen */
  if(int_perc < 0) {
    int_perc = 0;
    percentage = 0;
  } else if(int_perc > 100) {
    int_perc = 100;
    percentage = 100;
  }

  if(!showFlows)
    flowBuf[0] = '\0';
  else
    safe_snprintf(__FILE__, __LINE__, flowBuf, sizeof(flowBuf),
		  "</TD><TD "TD_BG" ALIGN=RIGHT WIDTH=50>%s",
		  formatPkts(flows, tmpBuf, sizeof(tmpBuf)));

  if(!showRRD)
    rrdBuf[0] = '\0';
  else {
    safe_snprintf(__FILE__, __LINE__, rrdBuf, sizeof(rrdBuf), "%s/interfaces/%s/IP_%sBytes.rrd",
		  myGlobals.rrdPath != NULL ? myGlobals.rrdPath : ".",
		  myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName,
		  label);

    revertSlashIfWIN32(rrdBuf, 0);

    if(stat(rrdBuf, &statbuf) == 0) {
      time_t now = time(NULL);

      safe_snprintf(__FILE__, __LINE__, rrdBuf, sizeof(rrdBuf),
		    "<p><table border=0>"
		    "<tr><td align=left><IMG SRC=\"/plugins/rrdPlugin?action=arbreq&which=graph"
		    "&arbfile=IP_%sBytes&arbiface=%s&arbip=&start=now-12h&end=now&counter=&title=\" BORDER=0></td><td>"
		    "<A HREF=\"/plugins/rrdPlugin?mode=zoom&action=arbreq&which=graph&arbfile=IP_%sBytes"
		    "&arbiface=%s&arbip=&start=%d&end=%d&counter=&title=\">"
		    "&nbsp;<IMG valign=top class=tooltip SRC=graph_zoom.gif border=0></A></td></tr></table>\n",
		    _label, myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName,
		    _label, myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName,
		    now-12*3600, now);
    } else {
      rrdBuf[0] = '\0';
      /* traceEvent(CONST_TRACE_INFO,  "-> Unable to find file '%s'", rrdBuf); */
    }
  }

  if(showRRD && (rrdBuf[0] != '\0')) {
    int_perc = 0; /* Trick for avoid showing the bar */
  }


  switch(int_perc) {
  case 0:
    safe_snprintf(__FILE__, __LINE__, buf, bufLen, "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT WIDTH=150 "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT WIDTH=50>%s %s</TD><TD "TD_BG" ALIGN=RIGHT WIDTH=50>%.1f%%</TD>"
		  "<TD "TD_BG" WIDTH=260 nowrap>&nbsp;%s</TD></TR>\n\n",
		  getRowColor(), label, formatKBytes(total, formatBuf, sizeof(formatBuf)), flowBuf, percentage, rrdBuf);
    break;
  case 100:
    safe_snprintf(__FILE__, __LINE__, buf, bufLen, "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT WIDTH=150 "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT WIDTH=50>%s %s</TD><TD "TD_BG" ALIGN=RIGHT WIDTH=50>100%%</TD>"
		  "<TD "TD_BG" WIDTH=260 nowrap><IMG ALT=\"100%%\" ALIGN=MIDDLE SRC=\"/gauge.jpg\" WIDTH=260 HEIGHT=12>%s"
		  "</TD></TR>\n\n",
		  getRowColor(), label, formatKBytes(total, formatBuf, sizeof(formatBuf)), flowBuf, rrdBuf);
    break;
  default:
    safe_snprintf(__FILE__, __LINE__, buf, bufLen, "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT WIDTH=150 "DARK_BG">%s</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT WIDTH=50>%s %s</TD><TD "TD_BG" ALIGN=RIGHT WIDTH=50>%.1f%%</TD>"
		  "<TD "TD_BG" WIDTH=260 nowrap><TABLE BORDER=0 "TABLE_DEFAULTS" CELLPADDING=0 CELLSPACING=0>"
		  "<TR "TR_ON"><TD nowrap><IMG ALIGN=MIDDLE ALT=\"%.1f%%\" SRC=\"/gauge.jpg\" WIDTH=\"%d\" HEIGHT=12>%s"
		  "</TD><TD "TD_BG" nowrap ALIGN=CENTER WIDTH=\"%d\" %s>"
		  "<P>&nbsp;</TD></TR>\n</TABLE>"TABLE_OFF"</TD></TR>\n\n",
		  getRowColor(), label, formatKBytes(total, formatBuf, sizeof(formatBuf)),
		  flowBuf, percentage,
		  percentage, (260*int_perc)/100, rrdBuf,
		  (260*(100-int_perc))/100, getActualRowColor());
  }

  sendString(buf);
}

/* *********************************** */

void printHostHourlyTrafficEntry(HostTraffic *el, int i,
				 Counter tcSent, Counter tcRcvd) {
  float pctg;
  char buf[LEN_GENERAL_WORK_BUFFER], formatBuf[32];

  if(el->trafficDistribution == NULL)
    return;

  safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER, "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
	      formatBytes(el->trafficDistribution->last24HoursBytesSent[i].value, 0, formatBuf, sizeof(formatBuf)));
  sendString(buf);

  if(tcSent > 0)
    pctg = (float)(el->trafficDistribution->last24HoursBytesSent[i].value*100)/(float)tcSent;
  else
    pctg = 0;

  safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER, "<TD ALIGN=RIGHT %s>%.1f %%</TD>",
	   getBgPctgColor(pctg), pctg);
  sendString(buf);

  safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER, "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
	   formatBytes(el->trafficDistribution->last24HoursBytesRcvd[i].value, 0, formatBuf, sizeof(formatBuf)));
  sendString(buf);

 if(tcRcvd > 0)
    pctg = (float)(el->trafficDistribution->last24HoursBytesRcvd[i].value*100)/(float)tcRcvd;
  else
    pctg = 0;

  safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER, "<TD ALIGN=RIGHT %s>%.1f %%</TD></TR>\n",
	      getBgPctgColor(pctg), pctg);
  sendString(buf);
}

/* ************************************ */

char* getNbNodeType(char nodeType) {

  switch(nodeType) {
  case 0x0:
    return("Workstation");
  case 0x20:
  default:
    return("Server");
  }

  return(""); /* NOTREACHED */
}

 /* ********************************** */

void printFlagedWarning(char *text) {
  char buf[LEN_GENERAL_WORK_BUFFER];

  safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER,
		"<center>\n"
		"<p><img class=reflect alt=\"Warning\" src=\"/warning.gif\"></p>\n"
		"<p><font color=\"#FF0000\" size=\"+1\">%s</font></p>\n"
		"</center>\n", text);
  sendString(buf);
}

/* ********************************** */

void printPageTitle(char *text) {
  sendString("<p>&nbsp;</p>\n");
  switch (myGlobals.ntopRunState) {
      case FLAG_NTOPSTATE_RUN:
          break;

      case FLAG_NTOPSTATE_STOPCAP:
          sendString("<center><font face=\"Helvetica, Arial, Sans Serif\" size=\"+1\"><b>"
                     "Packet capture stopped"
                     "</b></font></center>\n");
          break;

      case FLAG_NTOPSTATE_SHUTDOWN:
          sendString("<center><font face=\"Helvetica, Arial, Sans Serif\" size=\"+1\"><b>"
                     "ntop shutting down"
                     "</b></font></center>\n");
          break;

      case FLAG_NTOPSTATE_TERM:
          sendString("<center><font face=\"Helvetica, Arial, Sans Serif\" size=\"+1\"><b>"
                     "ntop stopped"
                     "</b></font></center>\n");
          break;

  }

  sendString("<center>\n<H1><font face=\"Helvetica, Arial, Sans Serif\">");
  sendString(text);
  sendString("</font></H1>\n</center>\n");
}

/* ******************************** */

void printSectionTitle(char *text) {
  sendString("<p>&nbsp;</p>\n"
             "<center>\n<H2><font face=\"Helvetica, Arial, Sans Serif\">");
  sendString(text);
  sendString("</font></H2>\n</center>\n");
}

/* ******************************** */

void printHostsCharacterization(void) {
  u_int a=0, b=0, c=0, d=0, e=0, f=0, g=0, h=0, i=0, l=0, unhealthy=0, totHosts=0;
  HostTraffic *el;
  char buf[LEN_GENERAL_WORK_BUFFER], hostLinkBuf[3*LEN_GENERAL_WORK_BUFFER], headerSent = 0;

  printHTMLheader("Local Hosts Characterization", NULL, 0);

  for(el = getFirstHost(myGlobals.actualReportDeviceId);
      el != NULL; el = getNextHost(myGlobals.actualReportDeviceId, el)) {
    if((broadcastHost(el) == 0) /* No broadcast addresses please */
       && (multicastHost(el) == 0) /* No multicast addresses please */
       && subnetPseudoLocalHost(el)) {
      totHosts++;

      if(el->community && (!isAllowedCommunity(el->community))) continue;

      if(isPrinter(el)
	 || isBridgeHost(el)
	 || nameServerHost(el) || isNtpServer(el)
	 || gatewayHost(el) || isVoIPHost(el)
	 || isSMTPhost(el) || isIMAPhost(el) || isPOPhost(el)
	 || isDirectoryHost(el)
	 || isFTPhost(el)
	 || isHTTPhost(el)
	 || isWINShost(el)
	 || isDHCPClient(el) || isDHCPServer(el)
	 || isP2P(el)
	 || (isHostHealthy(el) != 0)
	 ) {
	if(!headerSent) {
	  sendString("<center>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR "TR_ON" "DARK_BG"><TH "TH_BG">Host</TH>"
		     "<TH>Unhealthy<br>Host</TH>"
		     "<TH>L2 Switch<br>Bridge</TH>"
		     "<TH>Gateway</TH>"
		     "<TH>VoIP<br>Host</TH>"
		     "<TH>Printer</TH>"
		     "<TH>NTP/DNS<br>Server</TH>"
		     "<TH>SMTP/POP/IMAP<br>Server</TH>"
		     "<TH>Directory/FTP/HTTP<br>Server</TH>"
		     "<TH>DHCP/WINS<br>Server</TH>"
		     "<TH>DHCP<br>Client</TH>"
		     "<TH>P2P</TH>"
		     "</TR>\n\n"
		     );
	  headerSent = 1;
	}

	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH ALIGN=LEFT>%s</TH>",
		    getRowColor(),
		    makeHostLink(el, FLAG_HOSTLINK_TEXT_FORMAT, 0, 0,
				 hostLinkBuf, sizeof(hostLinkBuf)));
	sendString(buf);

	if(isHostHealthy(el) != 0) { sendString("<TD ALIGN=CENTER>X</TD>"); unhealthy++; } else sendString("<TD>&nbsp;</TD>");
	if(isBridgeHost(el)) { sendString("<TD ALIGN=CENTER>X</TD>"); a++; } else sendString("<TD>&nbsp;</TD>");
	if(gatewayHost(el)) { sendString("<TD ALIGN=CENTER>X</TD>"); b++; } else sendString("<TD>&nbsp;</TD>");
	if(isVoIPHost(el)) { sendString("<TD ALIGN=CENTER>X</TD>"); c++; } else sendString("<TD>&nbsp;</TD>");
	if(isPrinter(el)) { sendString("<TD ALIGN=CENTER>X</TD>"); d++; } else sendString("<TD>&nbsp;</TD>");
	if(nameServerHost(el) || isNtpServer(el)) { sendString("<TD ALIGN=CENTER>X</TD>"); e++; } else sendString("<TD>&nbsp;</TD>");
	if(isSMTPhost(el) || isIMAPhost(el) || isPOPhost(el)) { sendString("<TD ALIGN=CENTER>X</TD>"); f++; } else sendString("<TD>&nbsp;</TD>");
	if(isDirectoryHost(el) || isFTPhost(el) || isHTTPhost(el)) { sendString("<TD ALIGN=CENTER>X</TD>"); g++; } else sendString("<TD>&nbsp;</TD>");
	if(isDHCPServer(el) || isWINShost(el)) { sendString("<TD ALIGN=CENTER>X</TD>"); h++; } else sendString("<TD>&nbsp;</TD>");
	if(isDHCPClient(el)) { sendString("<TD ALIGN=CENTER>X</TD>"); i++; } else sendString("<TD>&nbsp;</TD>");
	if(isP2P(el)) { sendString("<TD ALIGN=CENTER>X</TD>"); l++; } else sendString("<TD>&nbsp;</TD>");

	sendString("</TR>\n\n");
      }
    }
  }

  if(!headerSent) {
    printNoDataYet();
  } else {
    sendString("<TR "TR_ON"><TH>Total</TH>");
    if(unhealthy > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                  "<TD ALIGN=CENTER>%d [%.1f %%]</TD>",
                  unhealthy, (float)(unhealthy*100)/(float)totHosts);
      sendString(buf);
    } else
      sendString("<TD>&nbsp;</TD>");

    if(a > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD ALIGN=CENTER>%d</TD>", a);
      sendString(buf);
    } else
      sendString("<TD>&nbsp;</TD>");
    if(b > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD ALIGN=CENTER>%d</TD>", b);
      sendString(buf);
    } else
      sendString("<TD>&nbsp;</TD>");
    if(c > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD ALIGN=CENTER>%d</TD>", c);
      sendString(buf);
    } else
      sendString("<TD>&nbsp;</TD>");
    if(d > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD ALIGN=CENTER>%d</TD>", d);
      sendString(buf);
    } else
      sendString("<TD>&nbsp;</TD>");
    if(e > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD ALIGN=CENTER>%d</TD>", e);
      sendString(buf);
    } else
      sendString("<TD>&nbsp;</TD>");
    if(f > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD ALIGN=CENTER>%d</TD>", f);
      sendString(buf);
    } else
      sendString("<TD>&nbsp;</TD>");
    if(g > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD ALIGN=CENTER>%d</TD>", g);
      sendString(buf);
    } else
      sendString("<TD>&nbsp;</TD>");
    if(h > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD ALIGN=CENTER>%d</TD>", h);
      sendString(buf);
    } else
      sendString("<TD>&nbsp;</TD>");
    if(i > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD ALIGN=CENTER>%d</TD>", i);
      sendString(buf);
    } else
      sendString("<TD>&nbsp;</TD>");
    if(l > 0) {
          safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD ALIGN=CENTER>%d</TD>", i);
          sendString(buf);
    } else
      sendString("<TD>&nbsp;</TD>");
	sendString("</TABLE></CENTER>\n");
  }
}

/* ******************************** */

static void printFingerprintCounts(int countScanned, int countWithoutFP, int countBroadcast,
				   int countMulticast, int countRemote, int countNotIP,
				   int countUnknownFP, int unknownFPsEtc, int countCantResolve,
				   int fingerprintRemote,
				   char *unknownFPs) {
  char buf[LEN_GENERAL_WORK_BUFFER];
  struct tm t;

  sendString("<p><hr><p>\n");

  printSectionTitle("Statistics");

  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
              "<center>\n<table border=1 "TABLE_DEFAULTS">\n"
              "<tr><th colspan=\"2\"><i>Scanned</i></th></tr>\n"
              "<tr><td>Hosts</td><td align=\"right\">%d</td></tr>\n"
              "<tr><th colspan=\"2\"><i>Less:</i></th></tr>\n"
              "<tr><td>No fingerprint</td><td align=\"right\">%d</td></tr>\n"
              "<tr><td>Broadcast</td><td align=\"right\">%d</td></tr>\n"
              "<tr><td>Multicast</td><td align=\"right\">%d</td></tr>\n",
              countScanned,
              countWithoutFP,
              countBroadcast,
              countMulticast);
  sendString(buf);

  if(fingerprintRemote != TRUE) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                "<tr><td>Remote</td><td align=\"right\">%d</td></tr>\n",
                countRemote);
    sendString(buf);
  }

  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
              "<tr><td>Non IP host</td><td align=\"right\">%d</td></tr>\n"
              "<tr><th colspan=\"2\"><i>Gives:</i></th></tr>\n"
              "<tr><td>Possible to report</td><td align=\"right\">%d</td></tr>\n",
              countNotIP,
              countScanned - countWithoutFP - countBroadcast - countMulticast
                           - countRemote - countNotIP);
  sendString(buf);

  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
              "<tr><td>Less: Can not resolve<sup>*</sup></td>"
                  "<td align=\"right\">%d</td></tr>\n",
              countCantResolve);
  sendString(buf);

  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
              "<tr><td>Less: Unknown Fingerprint<sup>**</sup></td>"
                  "<td align=\"right\">%d</td></tr>\n",
              countUnknownFP);
  sendString(buf);

  sendString("</td></tr>\n</table>\n<p><table border=0 width=80%%>\n");

  sendString("<tr><td><sup>*</sup>&nbsp;<i>Can not resolve</i>&nbsp;means "
             "either the fingerprint was incomplete, or we tried to resolve "
             "it on a previous scan and it was not on file. "
             "No further action will occur for these hosts.</td></tr>\n");

  sendString("<tr><td><sup>**</sup>&nbsp;<i>Unknown Fingerprints</i>&nbsp;means "
             "we have not tried to resolve them yet.\n"
             "<ul>");

  if((myGlobals.nextFingerprintScan > 0) &&
     (countUnknownFP > 0) &&
     (myGlobals.runningPref.debugMode != 1)) {
        strftime(buf, sizeof(buf),
                 CONST_LOCALE_TIMESPEC, localtime_r(&myGlobals.nextFingerprintScan, &t));
        sendString("<li>May be resolved during the next scan, scheduled for ");
        sendString(buf);
        sendString(" (approximately).</li>\n");
      }

  if(unknownFPs[0] != '\0') {
    unknownFPs[0]=' ';
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
              "<li>Are:</i>&nbsp;%s%s</li>\n",
              unknownFPs,
              unknownFPsEtc == 1 ? " ..." : "");
    sendString(buf);
  }

#ifdef CONST_ETTERCAP_HOMEPAGE
  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
             "<li><p>Click "
		"<a href=\"%s\" class=tooltip alt=\"Ettercap page at SourceForge\">here</a> "
             "to visit Ettercap's home page at SourceForge. "
             "And, click "
		"<a href=\"%s%s\" class=tooltip alt=\"Ettercap fingerprint upload page\">here</a> "
             " to upload new fingerprints, or download additional (unverified) ones.</li>\n",
             CONST_ETTERCAP_HOMEPAGE,
             CONST_ETTERCAP_HOMEPAGE, CONST_ETTERCAP_FINGERPRINT);
  sendString(buf);
#endif

  sendString("<li><p>You can use the 'make dnetter' command, part of the ntop source distribution, "
             "to download the latest fingerprint file from the Ettercap cvs.</p></li>\n");

  if(fingerprintRemote != TRUE)
    sendString("<tr><td>Fingerprinting of non-local hosts may be erroneous "
               "- routers and intermediate hosts can alter the characteristics used to "
               "determine the operating system. Unfortunately, this can also occur because "
               "of entries not in the signature file, " CONST_OSFINGERPRINT_FILE "(.gz) - "
               "and there's no way to tell."
               "\n<br>That said, if you would like to see a page with ALL host fingerprints, "
               "local and remote, click <a href=\"" CONST_HOSTS_REMOTE_FINGERPRINT_HTML
               "\"  class=tooltip title=\"All host fingerprints page\">here</a></td></tr>\n");

  sendString("</table></center>\n");
}

/* ******************************** */

void printHostsStats(int fingerprintRemote) {
  u_int idx, numEntries=0, maxHosts;
  HostTraffic *el, **tmpTable;
  OsNumInfo theOSs[MAX_NUM_OS];
  int i,
      countScanned=0,
      countWithoutFP=0,
      countBroadcast=0,
      countMulticast=0,
      countRemote=0,
      countNotIP=0,
      countUnknownFP=0,
      countCantResolve=0;
  char buf[LEN_GENERAL_WORK_BUFFER], hostLinkBuf[3*LEN_GENERAL_WORK_BUFFER];
  char unknownFPs[LEN_GENERAL_WORK_BUFFER];
  int unknownFPsEtc=0;

  memset(theOSs, 0, sizeof(theOSs));
  memset(unknownFPs, 0, sizeof(unknownFPs));

  if(fingerprintRemote == 1)
    printHTMLheader("All Host Fingerprints (Local+Remote)", NULL, BITFLAG_HTML_NO_REFRESH);
  else
    printHTMLheader("Local Host Fingerprints", NULL, BITFLAG_HTML_NO_REFRESH);

  printSectionTitle("OS Summary");

  if(myGlobals.device[myGlobals.actualReportDeviceId].dummyDevice) {
    printFlagedWarning("<I>Host statistics (OS fingerprinting) are not available for virtual interfaces</I>");
    return;
  }

  maxHosts = myGlobals.device[myGlobals.actualReportDeviceId].hosts.hostsno; /* save it as it can change */
  tmpTable = (HostTraffic**)mallocAndInitWithReportWarn(maxHosts*sizeof(HostTraffic*), "printHostsStats");
  if(tmpTable == NULL)
    return;

  for(el = getFirstHost(myGlobals.actualReportDeviceId);
      el != NULL; el = getNextHost(myGlobals.actualReportDeviceId, el)) {
    countScanned++;

    if(el->community && (!isAllowedCommunity(el->community))) continue;

    if(el->fingerprint == NULL) {
      countWithoutFP++;
      continue;
    }
    if(broadcastHost(el)) {
      /* No broadcast addresses please */
      countBroadcast++;
      continue;
    }
    if(multicastHost(el)) {
      /* No multicast addresses please */
      countMulticast++;
      continue;
    }
    if((!subnetPseudoLocalHost(el)) &&
       (fingerprintRemote != TRUE)) {
      /* Local only */
      countRemote++;
      continue;
    }
    if((el->fingerprint[0] != ':') /* fingerprint has not been computed */ &&
       ((el->hostNumIpAddress[0] == '\0') ||
        (addrnull(&el->hostIpAddress)))) {
      countNotIP++;
      continue;
    }

    if(el->fingerprint[0] != ':') setHostFingerprint(el);
    if(el->fingerprint[0] != ':') {
      countUnknownFP++;
      if(strstr(unknownFPs, el->fingerprint) == NULL) {
          if(strlen(unknownFPs) + strlen(el->fingerprint) > (sizeof(unknownFPs) - 5)) {
            unknownFPsEtc=1;
          } else {
            strncat(unknownFPs, ", ", (sizeof(unknownFPs) - strlen(unknownFPs) - 1));
            strncat(unknownFPs, el->fingerprint, (sizeof(unknownFPs) - strlen(unknownFPs) - 1));
          }
      }
      continue;
    }

    if((el->fingerprint[0] == ':') && (el->fingerprint[1] == '\0')) {
      /* Too short */
      countCantResolve++;
      continue;
    }

    tmpTable[numEntries++] = el;

    for(i=0; i<MAX_NUM_OS; i++) {
      if(theOSs[i].name == NULL) break;
      if(strcmp(theOSs[i].name, &el->fingerprint[1]) == 0) {
	  theOSs[i].num++;
	  break;
      }
    }

    if(theOSs[i].name == NULL) {
      theOSs[i].name = strdup(&el->fingerprint[1]);
      theOSs[i].num++;
    }

    if(numEntries >= maxHosts)
      break;
  }

  if(numEntries <= 0) {
      printNoDataYet();

      free(tmpTable);

      printFingerprintCounts(countScanned, countWithoutFP, countBroadcast,
                             countMulticast, countRemote, countNotIP,
                             countUnknownFP, unknownFPsEtc, countCantResolve,
                             fingerprintRemote,
                             unknownFPs);

      return;
  }

  myGlobals.columnSort = 0;
  qsort(tmpTable, numEntries, sizeof(HostTraffic*), cmpFctn);

  sendString("<CENTER>\n");
  sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR "TR_ON" "DARK_BG"><TH "TH_BG">Host</TH>");

  for(i=0; i<MAX_NUM_OS; i++) {
      if(theOSs[i].name == NULL)
	break;
      else {
        char *strtokState = NULL, *os, *word;
        int sentBR=0;

	sendString("<TH>");
        os = strdup(theOSs[i].name);

        word = strtok_r(os, " ", &strtokState);
        while(word != NULL) {
          if((sentBR++) > 0) sendString("<br>\n");
          sendString(word);
          word = strtok_r(NULL, " ", &strtokState);
        }

        free(os);
	sendString("</TH>");
      }
  }

  sendString("</TR>\n\n");

  for(idx=0; idx<numEntries; idx++) {
      el = tmpTable[idx];

      if(el != NULL) {
	char *tmpName1;
	tmpName1 = el->hostNumIpAddress;
	if((tmpName1[0] == '\0') || (strcmp(tmpName1, "0.0.0.0") == 0))
	  tmpName1 = myGlobals.separator;

	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH ALIGN=LEFT>%s</TH>",
		    getRowColor(),
		    makeHostLink(el, FLAG_HOSTLINK_TEXT_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)));
	sendString(buf);

	for(i=0; i<MAX_NUM_OS; i++) {
	  if(theOSs[i].name == NULL)
	    break;
	  else {
	    if(strcmp(theOSs[i].name, &el->fingerprint[1]) == 0) {
	      if((el->protocolInfo != NULL) && (el->protocolInfo->userList != NULL)) {
		sendString("<TD ALIGN=LEFT>");
		printUserList(el);
		sendString("<br>\n</TD>");
	      } else {
		if((el->nonIPTraffic != NULL) && (el->nonIPTraffic->nbDomainName != NULL)) {
		  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                              "<TD ALIGN=CENTER>[ %s ]</TD>",
                              el->nonIPTraffic->nbDomainName);
		  sendString(buf);
		} else
		  sendString("<TD ALIGN=CENTER>X</TD>");
	      }
	    } else
	      sendString("<TD>&nbsp;</TD>");
	  }
	} /* for */

	sendString("</TR>\n\n");
      }
  }

  sendString("</TABLE></center>\n<p>&nbsp;</p>");

  /* ********************************** */

  qsort(theOSs, MAX_NUM_OS, sizeof(OsNumInfo), cmpOSFctn);

  sendString("<center>" TABLE_ON "<table border=\"1\" " TABLE_DEFAULTS ">\n"
             "<tr "TR_ON" "DARK_BG"><th "TH_BG">OS</th>\n<th "TH_BG">Total</th></tr>\n");

  for(i=0; i<MAX_NUM_OS; i++) {
     if(theOSs[i].name != NULL) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                    "<tr><th align=\"left\">%s</th>\n"
                    "<td align=\"right\">%d</td></tr>\n",
                    theOSs[i].name, theOSs[i].num);
	sendString(buf);
	free(theOSs[i].name);
      }
  }

  sendString("</table>\n</center>\n");

  free(tmpTable);

  printFingerprintCounts(countScanned, countWithoutFP, countBroadcast,
                         countMulticast, countRemote, countNotIP,
                         countUnknownFP, unknownFPsEtc, countCantResolve,
                         fingerprintRemote,
                         unknownFPs);
}

/* ******************************************************** */

void printMutexStatus(int textPrintFlag, PthreadMutex *mutexId, char *mutexName) {
#ifdef MUTEX_DEBUG
  char buf[LEN_GENERAL_WORK_BUFFER], bufAttempt[64], bufLock[64], bufUnlock[64];
  struct tm t;

  if(mutexId->numLocks == 0) /* Mutex never used */
    return;
  memset(bufAttempt, 0, sizeof(bufAttempt));
  if(mutexId->attempt.time.tv_sec > 0) {
    strftime(bufAttempt, sizeof(bufAttempt), CONST_LOCALE_TIMESPEC,
	     localtime_r((const time_t*)&mutexId->attempt.time.tv_sec, &t));
    strncat(bufAttempt, "<br>\n", (sizeof(bufAttempt) - strlen(bufAttempt) - 1));
  }
  memset(bufLock, 0, sizeof(bufLock));
  if(mutexId->lock.time.tv_sec > 0) {
    strftime(bufLock, sizeof(bufLock), CONST_LOCALE_TIMESPEC,
	     localtime_r((const time_t*)&mutexId->lock.time.tv_sec, &t));
    strncat(bufLock, "<br>\n", (sizeof(bufLock) - strlen(bufLock) - 1));
  }
  memset(bufUnlock, 0, sizeof(bufUnlock));
  if(mutexId->unlock.time.tv_sec > 0) {
    strftime(bufUnlock, sizeof(bufUnlock), CONST_LOCALE_TIMESPEC,
	     localtime_r((const time_t*)&mutexId->unlock.time.tv_sec, &t));
    strncat(bufUnlock, "<br>\n", (sizeof(bufUnlock) - strlen(bufUnlock) - 1));
  }
  if(textPrintFlag == TRUE) {
    if(myGlobals.runningPref.disableMutexExtraInfo) {
        safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                    "Mutex %s is %s, locked: %u times.\n",
                    mutexName, mutexId->isLocked ? "locked" : "unlocked",
                    mutexId->numLocks);
        sendString(buf);
    } else if(mutexId->attempt.line > 0) {
        safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                    "Mutex %s is %s.\n"
                    "     locked: %u times, last was at %s %s:%d(%d %u)\n"
                    "     blocked: at %s:%d(%d %u)\n",
                    mutexName, mutexId->isLocked ? "locked" : "unlocked",
                    mutexId->numLocks,
                    bufLock,
                    mutexId->lock.file, mutexId->lock.line, mutexId->lock.pid, mutexId->lock.thread,
                    mutexId->attempt.file, mutexId->attempt.line, mutexId->attempt.pid, mutexId->attempt.thread);
		    sendString(buf);

        safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                    "     unlocked: %u times, last was %s:%d(%d %u)\n"
                    "     longest: %.6f sec from %s:%d\n",
                    mutexId->numReleases, mutexId->unlock.file, mutexId->unlock.line, mutexId->unlock.pid, mutexId->unlock.thread,
                    mutexId->maxLockedDuration, mutexId->max.file,
		    mutexId->max.line);
        sendString(buf);
    } else {
        safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                    "Mutex %s, is %s.\n"
                    "     locked: %u times, last was at %s %s:%d(%d %u)\n"
                    "     unlocked: %u times, last was at %s %s:%d(%d %u)\n"
                    "     longest: %.6f sec from %s:%d\n",
                    mutexName,
                    mutexId->isLocked ? "locked" : "unlocked",
                    mutexId->numLocks,
                    bufLock,
                    mutexId->lock.file, mutexId->lock.line, mutexId->lock.pid, mutexId->lock.thread,
                    mutexId->numReleases,
                    bufUnlock,
                    mutexId->unlock.file, mutexId->unlock.line, mutexId->unlock.pid, mutexId->unlock.thread,
                    mutexId->maxLockedDuration,
                    mutexId->max.file,
                    mutexId->max.line);
        sendString(buf);
    }
  } else {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                  "<tr><th align=\"left\">%s</th>\n<td align=\"center\">%s</td>\n",
                  mutexName,
                  mutexId->isLocked ? "<font color=\"RED\">locked</font>" : "unlocked");
    sendString(buf);

    if(!myGlobals.runningPref.disableMutexExtraInfo) {

      if(mutexId->attempt.line == 0) {
        sendString("<td>&nbsp;</TD>\n");
      } else {
        safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                      "<td align=\"right\">%s, %s:%d p:%d t:%u</td>\n",
                      bufAttempt,
                      mutexId->attempt.file, mutexId->attempt.line, mutexId->attempt.pid, mutexId->attempt.thread);
        sendString(buf);
      }
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                    "<td align=\"right\">%s %s:%d p:%d t:%u</td>\n",
                    bufLock,
                    mutexId->lock.file, mutexId->lock.line, mutexId->lock.pid, mutexId->lock.thread);
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                    "<td align=\"right\">%s %s:%d p:%d t:%u</td>\n",
                    bufUnlock,
                    mutexId->unlock.file, mutexId->unlock.line, mutexId->unlock.pid, mutexId->unlock.thread);
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                    "<td align=\"right\">%.6f seconds %s:%d</td>\n",
		    mutexId->maxLockedDuration, mutexId->max.file, mutexId->max.line);
      sendString(buf);

    }

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
                  "<td align=\"right\">%u / %u</td></tr>\n",
                  mutexId->numLocks, mutexId->numReleases);
    sendString(buf);
  }
#endif
}

/* ************************************************ */

void printPluginTrailer(char *left, char *middle) {
  sendString("<br>\n<hr>\n<br>\n<table border=\"0\" width=100%><tr>");

  if(left != NULL) {
    sendString("<td width=\"20%\">[ <a href=\"../" CONST_PLUGINS_HEADER);
    sendString(left);
    sendString("\">Refresh this data</a> ]");
    sendString("&nbsp;</td>\n");  /* So there's at least something in to cell */
  }

  sendString("<td align=\"left\">");

  sendString("&nbsp;");
  if(middle != NULL)
    sendString(middle);
  sendString("&nbsp;");

  sendString("</td>\n<td align=\"right\">"
             "&nbsp;[ Back to <a href=\"/" CONST_SHOW_PLUGINS_HTML "\">plugins</a> ]"
             "</td></tr></table>\n<br>\n");
}

/* ************************************ */

void buildMapLink(HostTraffic *el, char *buf, int buf_len) {
  if(privateIPAddress(el))
    buf[0] = '\0';
  else {
    safe_snprintf(__FILE__, __LINE__, buf, buf_len,
		  " <A class=external href=\"#\" title=\"Physical Host Location\" "
		  "onclick=\"window.open(\'%s?host=%s&IP=%s\', "
		  "\'Host Map\', \'height=530, width=750,toolbar=nodirectories=no,status=no,"
		  "menubar=no,scrollbars=no,resizable=no\'); return false;\">"
		  "<IMG SRC=/marker.png border=0></A>\n",
		  myGlobals.runningPref.mapperURL,
		  el->hostResolvedName, el->hostNumIpAddress);
  }
}

/* ************************************ */
