/*
 *  Copyright (C) 1998-2007 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"

#define NETWORK_VIEW         1
#define AS_VIEW              2
#define AS_GRAPH_VIEW        3

static int network_mode_sort;

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

#ifndef WIN32
static void ignoreSignal(int signalId) {
  closeNwSocket(&myGlobals.newSock);
  (void)signal(signalId, ignoreSignal);
}
#endif

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

void printBandwidthFooter(void) {
  sendString("<p><b>NOTE</b>:</p>\n<ul>"
	     "<li>You can <a href=\"" CONST_EDIT_PREFS "\">define</a> "
	     "new communities.</li>\n"
	     "<li>Click <a href=\"" CONST_HOST_SORT_NOTE_HTML "\">here</a> "
	     "for more information about host and domain sorting.</li>\n"
	     "<li>Bandwidth values are the percentage of the total bytes that "
	     "<b>ntop</b> has seen on the interface.  Hover the mouse to see the actual "
	     "value (rounded to the nearest full percentage point).  <i>The total of the "
	     "values will NOT be 100% as local traffic will be counted TWICE (once as "
	     "sent and again as received).</i></li>\n"
	     "<li>The SENT bandwidth is shown as "
	     "<img class=tooltip  align=\"absmiddle\" src=\"/gaugeS.jpg\" alt=\"Sent\" title=\"Sent\" WIDTH=\"25\" HEIGHT=\"12\">"
	     " and the RECEIVED bandwidth is shown as "
	     "<img class=tooltip align=\"absmiddle\" src=\"/gaugeR.jpg\" alt=\"Received\" title=\"Received\" WIDTH=\"25\" HEIGHT=\"12\"></li>\n"
	     "</ul></p>");
}

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

void checkReportDevice(void) {
  int i;
  char value[LEN_SMALL_WORK_BUFFER];

  /* Show device table */
  for(i=0; i<myGlobals.numDevices; i++) {
    traceEvent(CONST_TRACE_NOISY, "Device %2d. %-30s%s%s%s",
	       i,
	       myGlobals.device[i].humanFriendlyName != NULL ?
	       myGlobals.device[i].humanFriendlyName :
	       myGlobals.device[i].name,
	       myGlobals.device[i].virtualDevice ? " (virtual)" : "",
	       myGlobals.device[i].dummyDevice ? " (dummy)" : "",
	       myGlobals.device[i].activeDevice ? " (active)" : "");
  }

  /* Corrections on stored value */
  if(myGlobals.runningPref.mergeInterfaces) {
    traceEvent(CONST_TRACE_NOISY,
               "INITWEB: Merging interfaces, reporting device forced to 0");
    storePrefsValue("actualReportDeviceId", "0");
  } else if(fetchPrefsValue("actualReportDeviceId", value, sizeof(value)) == -1) {
    traceEvent(CONST_TRACE_NOISY,
               "INITWEB: Reporting device not set, defaulting to 0");
    storePrefsValue("actualReportDeviceId", "0");
  } else if(atoi(value) >= myGlobals.numDevices) {
    traceEvent(CONST_TRACE_WARNING,
               "INITWEB: Reporting device (%d) invalid (> max, %d), defaulting to 0",
	       atoi(value), myGlobals.numDevices);
    storePrefsValue("actualReportDeviceId", "0");
  }

  /* Retrieve it */
  if(fetchPrefsValue("actualReportDeviceId", value, sizeof(value)) == -1) {
    myGlobals.actualReportDeviceId = 0;
  } else {
    myGlobals.actualReportDeviceId = atoi(value);
  }

  if(myGlobals.device[myGlobals.actualReportDeviceId].virtualDevice) {
    /* Bad idea, set to 1st non-virtual device */
    traceEvent(CONST_TRACE_WARNING,
               "INITWEB: Reporting device (%d) invalid (virtual), using 1st non-virtual device", i);
    for(i=0; i<myGlobals.numDevices; i++) {
      if(!myGlobals.device[i].virtualDevice) {
        myGlobals.actualReportDeviceId = i;
        safe_snprintf(__FILE__, __LINE__, value, sizeof(value), "%d", i);
        storePrefsValue("actualReportDeviceId", value);
	break;
      }
    }
  }
}

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

void initReports(void) {
  myGlobals.columnSort = 0;

  checkReportDevice();

  traceEvent(CONST_TRACE_INFO,
	     "Note: Reporting device initally set to %d [%s]%s",
	     myGlobals.actualReportDeviceId,
	     myGlobals.device[myGlobals.actualReportDeviceId].humanFriendlyName != NULL ?
	     myGlobals.device[myGlobals.actualReportDeviceId].humanFriendlyName :
	     myGlobals.device[myGlobals.actualReportDeviceId].name,
	     myGlobals.runningPref.mergeInterfaces ? " (merged)" : "");
}

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

int reportValues(time_t *lastTime) {
  if(myGlobals.runningPref.maxNumLines <= 0)
    myGlobals.runningPref.maxNumLines = CONST_NUM_TABLE_ROWS_PER_PAGE;

  *lastTime = time(NULL) + myGlobals.runningPref.refreshRate;

  /*
    Make sure that the other flags are't set. They have
    no effect in web mode
  */
  if(myGlobals.runningPref.refreshRate == 0)
    myGlobals.runningPref.refreshRate = DEFAULT_NTOP_AUTOREFRESH_INTERVAL;
  else if(myGlobals.runningPref.refreshRate < PARM_MIN_WEBPAGE_AUTOREFRESH_TIME)
    myGlobals.runningPref.refreshRate = PARM_MIN_WEBPAGE_AUTOREFRESH_TIME;

  return(0);
}

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

void addPageIndicator(char *url, u_int pageNum,
		      u_int numEntries, u_int linesPerPage,
		      int revertOrder, int numCol, int netmode) {
  char buf[LEN_GENERAL_WORK_BUFFER/2], prevBuf[LEN_GENERAL_WORK_BUFFER/2],
    nextBuf[LEN_GENERAL_WORK_BUFFER/2], shortBuf[16], separator;
  int numPages = (numEntries+myGlobals.runningPref.maxNumLines-1)/myGlobals.runningPref.maxNumLines;
  int actPage  = pageNum+1;

  if(numPages <= 1) return;

  if(strchr(url, '?') != NULL)
    separator = '&';
  else
    separator = '?';

  if(revertOrder == -1)
    shortBuf[0] = '\0';
  else {
    safe_snprintf(__FILE__, __LINE__, shortBuf, sizeof(shortBuf),
		  "%s%d", revertOrder == 1 ? "-" : "", numCol);
  }

  if(pageNum >= 1) {
    safe_snprintf(__FILE__, __LINE__, prevBuf, sizeof(prevBuf),
		  "<A HREF=\"%s%cpage=0&netmode=%d&col=%s\"><IMG SRC=/fback.gif BORDER=0 "TABLE_DEFAULTS" ALIGN=vmiddle ALT=\"Back to first page\"></A> "
		  "<A HREF=\"%s%cpage=%d&netmode=%dcol=%s\"><IMG SRC=/back.gif BORDER=0 "TABLE_DEFAULTS" ALIGN=vmiddle ALT=\"Prior page\"></A>",
		  url, separator, netmode, shortBuf, 
		  url, separator, pageNum-1, netmode, shortBuf);
  } else
    prevBuf[0] = '\0';

  if(actPage < numPages) {
    safe_snprintf(__FILE__, __LINE__, nextBuf, sizeof(nextBuf),
		  "<A HREF=\"%s%cpage=%d&netmode=%d&col=%s\"><IMG SRC=/forward.gif BORDER=0 "TABLE_DEFAULTS" ALIGN=vmiddle ALT=\"Next Page\"></A> "
		  "<A HREF=\"%s%cpage=%d&netmode=%d&col=%s\"><IMG SRC=/fforward.gif BORDER=0 "TABLE_DEFAULTS" ALIGN=vmiddle ALT=\"Forward to last page\"></A>",
		  url, separator, pageNum+1, netmode, shortBuf, 
		  url, separator, numPages-1, netmode, shortBuf);
  }  else
    nextBuf[0] = '\0';

  sendString("<P><FONT FACE=Helvetica><B>");
  sendString(prevBuf);

  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), " [ %d / %d ] ", actPage, numPages);
  sendString(buf);

  sendString(nextBuf);
  sendString("</B></FONT>\n");
}

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

void printTrafficSummary (int revertOrder) {
  Counter unicastPkts;
  int i;
  char buf[LEN_GENERAL_WORK_BUFFER], formatBuf[32], formatBuf1[32];
  struct pcap_stat pcapStat;

  unicastPkts = 0;
  printHTMLheader("Global Traffic Summary", NULL, 0);

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

  sendString("<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Network Interface(s)</TH>"
	     "<TD "TD_BG" ALIGN=RIGHT>");

  sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"100%\">\n<TR "TR_ON" "DARK_BG"><TH "TH_BG" "DARK_BG">Name</TH>"
	     "<TH "TH_BG" "DARK_BG">Device</TH><TH "TH_BG" "DARK_BG">Type</TH>"
	     "<TH "TH_BG" "DARK_BG">Speed</TH><TH "TH_BG" "DARK_BG">Sampling Rate</TH><TH "TH_BG" "DARK_BG">MTU</TH>"
	     "<TH "TH_BG" "DARK_BG">Header</TH><TH "TH_BG" "DARK_BG">Address</TH>");

#ifdef INET6
  sendString("<TH "TH_BG" "DARK_BG">IPv6 Addresses</TH></TR>\n");
#endif

  for(i=0; i<myGlobals.numDevices; i++) {
    if(myGlobals.device[i].activeDevice) {
      char buf1[128];
      NtopIfaceAddr *ifaddr;
      
      if(myGlobals.device[i].sflowGlobals || myGlobals.device[i].netflowGlobals)
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" ALIGN=CENTER><TD "TD_BG">"
		      "%s</TD>",
		      myGlobals.device[i].humanFriendlyName[0] != '\0' ?
		      myGlobals.device[i].humanFriendlyName : "&nbsp;");
      else
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" ALIGN=CENTER><TD "TD_BG">"
		      "%s <A HREF=\""CONST_EDIT_PREFS"?key=device.name.%s\">"
		      "<img class=tooltip alt=\"Change name\" src=/"CONST_EDIT_IMG" border=\"0\"></A></TD>",
		      myGlobals.device[i].uniqueIfName[0] != '\0' ? myGlobals.device[i].uniqueIfName : "&nbsp;",
		      myGlobals.device[i].name);

	sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%s</TD>",
		    myGlobals.device[i].name);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%s%s</TD>",
		    getNwInterfaceType(i), myGlobals.device[i].virtualDevice ? " virtual" : "");
      sendString(buf);

      sendString("<TD "TD_BG" ALIGN=RIGHT nowrap>&nbsp;");
      if(myGlobals.device[i].deviceSpeed > 0) {
	/* The speed is known */
	sendString(formatAdapterSpeed(myGlobals.device[i].deviceSpeed, formatBuf, sizeof(formatBuf)));
      } else
	sendString("&nbsp;");
      sendString("</TD>");

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%d</TD>",
		    myGlobals.device[i].samplingRate);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%d</TD>",
		    myGlobals.mtuSize[myGlobals.device[i].datalink]);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%d</TD>",
		    myGlobals.headerSize[myGlobals.device[i].datalink]);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%s</TD>",
		    _intoa(myGlobals.device[i].ifAddr, buf1, sizeof(buf1)));
      sendString(buf);

#ifdef INET6
      sendString("<TD ALIGN=LEFT>");
      if(myGlobals.device[i].v6Addrs > 0) {
	for(ifaddr = myGlobals.device[i].v6Addrs;
	    ifaddr != NULL; ifaddr = ifaddr->next) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s/%d<br>",
			_intop(&ifaddr->af.inet6.ifAddr, buf1, sizeof(buf1)),
			ifaddr->af.inet6.prefixlen);
	  sendString(buf);
	}
      } else
	sendString("&nbsp;");

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

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

  if(myGlobals.runningPref.domainName[0] != '\0') {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Local Domain Name</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s&nbsp;</TD></TR>\n",
		  myGlobals.runningPref.domainName);
    sendString(buf);
  }

  if(myGlobals.runningPref.rFileName == NULL) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Sampling Since</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s [%s]</TD></TR>\n",
		  ctime(&myGlobals.initialSniffTime),
		  formatSeconds(time(NULL)-myGlobals.initialSniffTime, formatBuf, sizeof(formatBuf)));
    sendString(buf);
  }
  else {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Sampling Since</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		  ctime(&myGlobals.initialSniffTime));
    sendString(buf);

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" align=left "DARK_BG">Last Packet Seen</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s [%s]</TD></TR>\n",
		  ctime((time_t *)&myGlobals.lastPktTime),
		  formatSeconds(myGlobals.lastPktTime.tv_sec-myGlobals.initialSniffTime, formatBuf, sizeof(formatBuf)));
    sendString(buf);
  }

  if((i = numActiveSenders(myGlobals.actualReportDeviceId)) > 0) {
    /* Do NOT add a '/' at the end of the path because Win32 will complain about it */
    struct stat statbuf;

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s/interfaces/%s",
		  myGlobals.rrdPath != NULL ? myGlobals.rrdPath : ".",
		  myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName);
        
    revertSlashIfWIN32(buf, 0);
   
    if(stat(buf, &statbuf) != 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Active End Nodes</TH>"
		    "<TD "TD_BG" ALIGN=RIGHT>%u</TD></TR>\n", i);
    } else
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Active End Nodes</TH>"
		    "<TD "TD_BG" ALIGN=RIGHT>"		    
		    "%u <A HREF=\"/plugins/rrdPlugin?action=arbreq&which=graph&arbfile=knownHostsNum&arbiface=%s&start=%u&end=%u&counter=&title=%s&mode=zoom\">"
		    "<IMG valign=top class=tooltip SRC=graph.gif border=0></A>"
		    "</TD></TR>\n", 
		    i, myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, (unsigned int)(myGlobals.actTime-3600), 
		    (unsigned int)myGlobals.actTime, "Active+End+Nodes");

    
    sendString(buf);
  }

  if((myGlobals.runningPref.currentFilterExpression != NULL)
     && (myGlobals.runningPref.currentFilterExpression[0] != '\0')) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Traffic Filter</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		  myGlobals.runningPref.currentFilterExpression);


    sendString(buf);
  }

#ifndef EMBEDDED
  if(myGlobals.numDevices > 1) {
    int found = 0;

    for(i=0; i<myGlobals.numDevices; i++)
      if(myGlobals.device[i].ethernetPkts.value > 0) {
	found = 1;
	break;
      }

    if(found) {
      sendString("<TR "TR_ON" BGCOLOR=white><TH "TH_BG" ALIGN=CENTER COLSPAN=3 BGCOLOR=white>"
		 "<iframe frameborder=0 SRC=\"" CONST_PIE_INTERFACE_DIST CHART_FORMAT "\"></iframe></TH></TR>\n");
    }
  }
#endif

  if(myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value > 0) {
    unicastPkts = myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value
      - myGlobals.device[myGlobals.actualReportDeviceId].broadcastPkts.value
      - myGlobals.device[myGlobals.actualReportDeviceId].multicastPkts.value;

    if(myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value <= 0)
      myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value = 1;

    if(myGlobals.device[myGlobals.actualReportDeviceId].pcapPtr != NULL) {
      if(pcap_stats(myGlobals.device[myGlobals.actualReportDeviceId].pcapPtr, &pcapStat) >= 0) {
	Counter realDropped;

	/*
	  Recent libpcap versions do not report total/cumulative values
	  but their value is reset everytime is read
	*/

	if(myGlobals.device[myGlobals.actualReportDeviceId].receivedPkts.value > pcapStat.ps_recv) {
	  /* The counter is reset at each run */
	  realDropped = (myGlobals.device[myGlobals.actualReportDeviceId].pcapDroppedPkts.value += pcapStat.ps_drop);

	} else {
	  /* The counter is NOT reset at each run */
	  myGlobals.device[myGlobals.actualReportDeviceId].pcapDroppedPkts.value = pcapStat.ps_drop;
	  realDropped = myGlobals.device[myGlobals.actualReportDeviceId].pcapDroppedPkts.value
	    - myGlobals.device[myGlobals.actualReportDeviceId].initialPcapDroppedPkts.value;
	}

	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		      "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Dropped&nbsp;(libpcap)</th>"
		      "<TD "TD_BG" align=right>%s(%.1f%%)</td></TR>\n",
		      getRowColor(),
		      formatPkts(realDropped, formatBuf, sizeof(formatBuf)), (float)(realDropped*100)
		      /(float)myGlobals.device[myGlobals.actualReportDeviceId].receivedPkts.value);
	sendString(buf);
      }
    }

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Dropped&nbsp;(ntop)</th>"
		  "<TD "TD_BG" align=right>%s(%.1f%%)</td></TR>\n",
		  getRowColor(),
		  formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].droppedPkts.value,
			     formatBuf, sizeof(formatBuf)),
                  (float)(myGlobals.device[myGlobals.actualReportDeviceId].droppedPkts.value*100)
                  /(float)myGlobals.device[myGlobals.actualReportDeviceId].receivedPkts.value);
    sendString(buf);

    if(!myGlobals.runningPref.printFcOnly) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Unicast</th>"
		    "<TD "TD_BG" align=right>%s(%.1f%%)</td></TR>\n",
		    getRowColor(),
		    formatPkts(unicastPkts, formatBuf, sizeof(formatBuf)),
		    (float)(100*unicastPkts)/(float)myGlobals.device[myGlobals.actualReportDeviceId].
		    ethernetPkts.value);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Broadcast</th>"
		    "<TD "TD_BG" align=right>%s(%.1f%%)</td></TR>\n",
		    getRowColor(),
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].broadcastPkts.value, formatBuf, sizeof(formatBuf)),
		    (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].broadcastPkts.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Multicast</th>"
		    "<TD "TD_BG" align=right>%s(%.1f%%)</td></TR>\n",
		    getRowColor(),
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].multicastPkts.value, formatBuf, sizeof(formatBuf)),
		    (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].multicastPkts.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Packets&nbsp;too&nbsp;long [> %d]</th>"
		    "<TD "TD_BG" align=right>%s(%.1f%%)</td></TR>\n",
		    getRowColor(), myGlobals.mtuSize[myGlobals.device[myGlobals.actualReportDeviceId].datalink],
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.tooLong.value, formatBuf, sizeof(formatBuf)),
		    (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.tooLong.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Bad&nbsp;Packets&nbsp;(Checksum)</th>"
		    "<TD "TD_BG" align=right>%s(%.1f%%)</td></TR>\n",
		    getRowColor(),
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.badChecksum.value, formatBuf, sizeof(formatBuf)),
		    (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
			    rcvdPktStats.badChecksum.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value);
      sendString(buf);
    }

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

    if(!myGlobals.runningPref.printIpOnly &&
       myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value > 0) {
      if(myGlobals.device[myGlobals.actualReportDeviceId].pcapPtr != NULL) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Total</th>"
		      "<TD "TD_BG" align=right COLSPAN=2>%s [%s Pkts]</td></TR>\n",
		      getRowColor(),
		      formatBytes(myGlobals.device[myGlobals.actualReportDeviceId].fcBytes.value, 1,
				  formatBuf, sizeof(formatBuf)),
		      formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
				 formatBuf1, sizeof(formatBuf1)));
	sendString(buf);
#ifdef NOT_YET
	if(myGlobals.device[myGlobals.actualReportDeviceId].droppedPkts.value > 0) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			"<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Dropped&nbsp;by&nbsp;the&nbsp;kernel</th>"
			"<TD "TD_BG" COLSPAN=2 align=right>%s [%.2f %%]</td></TR>\n",
			getRowColor(),
			formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].droppedPkts.value,
				   formatBuf, sizeof(formatBuf)),
			(float)(myGlobals.device[myGlobals.actualReportDeviceId].droppedPkts.value*100)
			/(float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value);
	  sendString(buf);
	}
#endif
      }

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Unicast</th>"
		    "<TD "TD_BG" align=right>%s(%.1f%%)</td></TR>\n",
		    getRowColor(),
		    formatPkts(unicastPkts, formatBuf, sizeof(formatBuf)),
		    (float)(100*unicastPkts)/(float)myGlobals.device[myGlobals.actualReportDeviceId].
		    fcPkts.value);
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Broadcast</th>"
		    "<TD "TD_BG" align=right>%s(%.1f%%)</td></TR>\n",
		    getRowColor(),
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].fcBroadcastPkts.value,
			       formatBuf, sizeof(formatBuf)),
		    (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].fcBroadcastPkts.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value);
      sendString(buf);

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

    if(!myGlobals.device[myGlobals.actualReportDeviceId].dummyDevice) {
      updateThpt(0);

      sendString("<TR><TH "TH_BG" ALIGN=LEFT "DARK_BG">Network Load</TH><TD "TH_BG">\n<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"100%\">");
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Actual</th><TD "TD_BG" align=right>%s</td>"
		    "<TD "TD_BG" align=right>%.1f&nbsp;Pkt/s</td></TR>\n",
		    getRowColor(), formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].actualThpt,
						    1, formatBuf, sizeof(formatBuf)),
		    myGlobals.device[myGlobals.actualReportDeviceId].actualPktsThpt);
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Last Minute</th>"
		    "<TD "TD_BG" align=right>%s</td>"
		    "<TD "TD_BG" align=right>%.1f&nbsp;Pkt/s</td></TR>\n",
		    getRowColor(), formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].lastMinThpt,
						    1, formatBuf, sizeof(formatBuf)),
		    myGlobals.device[myGlobals.actualReportDeviceId].lastMinPktsThpt);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Last 5 Minutes</th>"
		    "<TD "TD_BG" align=right>%s</td>"
		    "<TD "TD_BG" align=right>%.1f&nbsp;Pkt/s</td></TR>\n",
		    getRowColor(), formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].lastFiveMinsThpt,
						    1, formatBuf, sizeof(formatBuf)),
		    myGlobals.device[myGlobals.actualReportDeviceId].lastFiveMinsPktsThpt);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Peak</th>"
		    "<TD "TD_BG" align=right>%s</td>"
		    "<TD "TD_BG" align=right>%.1f&nbsp;Pkt/s</td></TR>\n",
		    getRowColor(), formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].peakThroughput,
						    1, formatBuf, sizeof(formatBuf)),
		    myGlobals.device[myGlobals.actualReportDeviceId].peakPacketThroughput);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Average</th>"
		    "<TD "TD_BG" align=right>%s</td>"
		    "<TD "TD_BG" align=right>%.1f&nbsp;Pkt/s</td></TR>\n",
		    getRowColor(),
		    formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value/
				     (myGlobals.actTime-myGlobals.initialSniffTime+1), 1, formatBuf, sizeof(formatBuf)),
		    /* Bug below fixed courtesy of Eddy Lai <eddy@ModernTerminals.com> */
		    ((float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value/
		     (float)(myGlobals.actTime-myGlobals.initialSniffTime+1)));
      sendString(buf);

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

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

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

  if(!myGlobals.runningPref.printFcOnly) {
    printProtoTraffic(FALSE);
    sendString("<p>\n");
    printIpProtocolDistribution(FLAG_HOSTLINK_HTML_FORMAT, revertOrder, FALSE);
  }
  if(!myGlobals.runningPref.printIpOnly) {
    sendString("<p>\n");
    printFcTrafficSummary(0);
  }
}

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

void printTrafficStatistics(int revertOrder) {
  Counter unicastPkts, avgPktLen;
  int i;
  char buf[LEN_GENERAL_WORK_BUFFER], formatBuf[32], formatBuf1[32];
  struct stat statbuf;
  struct pcap_stat pcapStat;

  unicastPkts = 0;
  printHTMLheader("Global Traffic Statistics", NULL, 0);

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

  sendString("<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Network Interface(s)</TH>"
	     "<TD "TD_BG" ALIGN=RIGHT>");

  sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"100%\">\n<TR "TR_ON" "DARK_BG"><TH "TH_BG" "DARK_BG">Name</TH>"
	     "<TH "TH_BG" "DARK_BG">Device</TH><TH "TH_BG" "DARK_BG">Type</TH>"
	     "<TH "TH_BG" "DARK_BG">Speed</TH><TH "TH_BG" "DARK_BG">Sampling Rate</TH><TH "TH_BG" "DARK_BG">MTU</TH>"
	     "<TH "TH_BG" "DARK_BG">Header</TH><TH "TH_BG" "DARK_BG">Address</TH>");

#ifdef INET6
  sendString("<TH "TH_BG" "DARK_BG">IPv6 Addresses</TH></TR>\n");
#endif

  for(i=0; i<myGlobals.numDevices; i++) {
    if(myGlobals.device[i].activeDevice) {
      char buf1[128];
      NtopIfaceAddr *ifaddr;

      if(myGlobals.device[i].sflowGlobals || myGlobals.device[i].netflowGlobals)
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" ALIGN=CENTER><TD "TD_BG">"
		      "%s</TD>",
		      myGlobals.device[i].humanFriendlyName[0] != '\0' ? 
		      myGlobals.device[i].humanFriendlyName : "&nbsp;");
      else
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" ALIGN=CENTER><TD "TD_BG">"
		      "%s <A HREF=\""CONST_EDIT_PREFS"?key=device.name.%s\">"
		      "<img class=tooltip alt=\"Change name\" src=/"CONST_EDIT_IMG" border=\"0\"></A></TD>",
		      myGlobals.device[i].uniqueIfName[0] != '\0' ? myGlobals.device[i].uniqueIfName : "&nbsp;",
		      myGlobals.device[i].name);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%s</TD>",
		    myGlobals.device[i].name);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%s%s</TD>",
		    getNwInterfaceType(i), myGlobals.device[i].virtualDevice ? " virtual" : "");
      sendString(buf);

      sendString("<TD "TD_BG" ALIGN=RIGHT nowrap>&nbsp;");
      if(myGlobals.device[i].deviceSpeed > 0) {
	/* The speed is known */
	sendString(formatAdapterSpeed(myGlobals.device[i].deviceSpeed, formatBuf, sizeof(formatBuf)));
      } else
	sendString("&nbsp;");
      sendString("</TD>");

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%d</TD>",
		    myGlobals.device[i].samplingRate);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%d</TD>",
		    myGlobals.mtuSize[myGlobals.device[i].datalink]);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%d</TD>",
		    myGlobals.headerSize[myGlobals.device[i].datalink]);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%s</TD>",
		    _intoa(myGlobals.device[i].ifAddr, buf1, sizeof(buf1)));
      sendString(buf);

#ifdef INET6
      sendString("<TD ALIGN=LEFT>");
      if(myGlobals.device[i].v6Addrs > 0) {
	for(ifaddr = myGlobals.device[i].v6Addrs;
	    ifaddr != NULL; ifaddr = ifaddr->next) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s/%d<br>",
			_intop(&ifaddr->af.inet6.ifAddr, buf1, sizeof(buf1)),
			ifaddr->af.inet6.prefixlen);
	  sendString(buf);
	}
      } else
	sendString("&nbsp;");

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


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

  if(myGlobals.runningPref.domainName[0] != '\0') {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Local Domain Name</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s&nbsp;</TD></TR>\n",
		  myGlobals.runningPref.domainName);
    sendString(buf);
  }

  if(myGlobals.runningPref.rFileName == NULL) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Sampling Since</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s [%s]</TD></TR>\n",
		  ctime(&myGlobals.initialSniffTime),
		  formatSeconds(time(NULL)-myGlobals.initialSniffTime, formatBuf, sizeof(formatBuf)));
    sendString(buf);
  }
  else {
	  time_t t = myGlobals.lastPktTime.tv_sec;

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Sampling Since</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		  ctime(&myGlobals.initialSniffTime));
    sendString(buf);

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" align=left "DARK_BG">Last Packet Seen</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s [%s]</TD></TR>\n",
		  ctime(&t),
		  formatSeconds(myGlobals.lastPktTime.tv_sec-myGlobals.initialSniffTime, formatBuf, sizeof(formatBuf)));
    sendString(buf);
  }

  if((i = numActiveSenders(myGlobals.actualReportDeviceId)) > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s/interfaces/%s",
		  myGlobals.rrdPath != NULL ? myGlobals.rrdPath : ".",
		  myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName);    
    
    revertSlashIfWIN32(buf, 0);
   
    if(stat(buf, &statbuf) != 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Active End Nodes</TH>"
		    "<TD "TD_BG" ALIGN=RIGHT>%u</TD></TR>\n", i);
    } else
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Active End Nodes</TH>"
		    "<TD "TD_BG" ALIGN=RIGHT>"		    
		    "%u <A HREF=\"/plugins/rrdPlugin?action=arbreq&which=graph&arbfile=knownHostsNum&arbiface=%s&start=%u&end=%u&counter=&title=%s&mode=zoom\">"
		    "<IMG valign=top class=tooltip SRC=graph.gif border=0></A>"
		    "</TD></TR>\n", 
		    i, myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, (unsigned int)(myGlobals.actTime-3600), 
		    (unsigned int)myGlobals.actTime, "Active+End+Nodes");

    
    sendString(buf);
  }

  if((myGlobals.runningPref.currentFilterExpression != NULL)
     && (myGlobals.runningPref.currentFilterExpression[0] != '\0')) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG">Traffic Filter</TH>"
		  "<TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		  myGlobals.runningPref.currentFilterExpression);
    sendString(buf);
  }

#ifndef EMBEDDED
  if(myGlobals.numDevices > 1) {
    int found = 0;

    for(i=0; i<myGlobals.numDevices; i++)
      if(myGlobals.device[i].ethernetPkts.value > 0) {
	found = 1;
	break;
      }

    if(found) {
	sendString("<TR "TR_ON" BGCOLOR=white><TH "TH_BG" ALIGN=CENTER COLSPAN=3 BGCOLOR=white>"
		   "<iframe frameborder=0 SRC=\"" CONST_PIE_INTERFACE_DIST CHART_FORMAT "\" width=400 height=250></iframe></TH></TR>\n");
    }
  }
#endif

  if(myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value > 0) {
    Counter dummyCounter;

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

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "Traffic Report for '%s' [<A HREF=/switch.html>switch</A>]",
		  myGlobals.device[myGlobals.actualReportDeviceId].humanFriendlyName);

    printSectionTitle(buf);
    sendString("<CENTER>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n");

    sendString("<TR><TH "TH_BG" align=left "DARK_BG">Packets</TH><TD "TH_BG">\n"
	       "<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"100%\">\n");

    unicastPkts = myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value
      - myGlobals.device[myGlobals.actualReportDeviceId].broadcastPkts.value
      - myGlobals.device[myGlobals.actualReportDeviceId].multicastPkts.value;

    if(myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value <= 0)
      myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value = 1;

    if(myGlobals.device[myGlobals.actualReportDeviceId].pcapPtr != NULL) {
      if(pcap_stats(myGlobals.device[myGlobals.actualReportDeviceId].pcapPtr, &pcapStat) >= 0) {
	/*
	  Recent libpcap versions do not report total/cumulative values
	  but their value is reset everytime is read
	*/

	if(myGlobals.device[myGlobals.actualReportDeviceId].receivedPkts.value > pcapStat.ps_recv) {
	  /* The counter is reset at each run */
	  myGlobals.device[myGlobals.actualReportDeviceId].pcapDroppedPkts.value += pcapStat.ps_drop;
	} else {
	  /* The counter is NOT reset at each run */
	  myGlobals.device[myGlobals.actualReportDeviceId].pcapDroppedPkts.value = pcapStat.ps_drop;
	}

	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		      "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Dropped&nbsp;(libpcap)</th>"
		      "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		      getRowColor(),
		      (float)(myGlobals.device[myGlobals.actualReportDeviceId].pcapDroppedPkts.value*100)
		      /(float)myGlobals.device[myGlobals.actualReportDeviceId].receivedPkts.value,
		      formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].pcapDroppedPkts.value,
				 formatBuf, sizeof(formatBuf)));
	sendString(buf);
      }
    }

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Dropped&nbsp;(ntop)</th>"
		  "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		  getRowColor(),
		  (float)(myGlobals.device[myGlobals.actualReportDeviceId].droppedPkts.value*100)
		  /(float)myGlobals.device[myGlobals.actualReportDeviceId].receivedPkts.value,
		  formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].droppedPkts.value,
			     formatBuf, sizeof(formatBuf)));
    sendString(buf);

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Total&nbsp;Received&nbsp;(ntop)</th>"
		  "<TD "TD_BG" COLSPAN=2 align=right>%s</td></TR>\n",
		  getRowColor(), formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].receivedPkts.value,
					    formatBuf, sizeof(formatBuf)));
    sendString(buf);

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Total Packets Processed</th>"
		  "<TD "TD_BG" COLSPAN=2 align=right>%s</td></TR>\n",
		  getRowColor(), formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
					    formatBuf, sizeof(formatBuf)));
    sendString(buf);

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Unicast</th>"
		  "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		  getRowColor(), (float)(100*unicastPkts)/(float)myGlobals.device[myGlobals.actualReportDeviceId].
		  ethernetPkts.value,
		  formatPkts(unicastPkts, formatBuf, sizeof(formatBuf)));
    sendString(buf);
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Broadcast</th>"
		  "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		  getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].broadcastPkts.value)/
		  (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		  formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].broadcastPkts.value, formatBuf, sizeof(formatBuf)));
    sendString(buf);

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Multicast</th>"
		  "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		  getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].multicastPkts.value)/
		  (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		  formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].multicastPkts.value, formatBuf, sizeof(formatBuf)));
    sendString(buf);

#ifndef EMBEDDED
    if(myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value > 0)
      sendString("<TR "TR_ON" BGCOLOR=white><TH "TH_BG" ALIGN=CENTER COLSPAN=3 BGCOLOR=white>"
		 "<iframe frameborder=0 SRC=\"" CONST_PIE_PKT_CAST_DIST CHART_FORMAT "\" width=400 height=250></iframe></TH></TR>\n");
#endif

    if(!myGlobals.device[myGlobals.actualReportDeviceId].dummyDevice) {
      /*
	Very rudimental formula. Note that as specified in RMON, packets smaller
	than 64 or larger than 1518 octets are not counted.
      */
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Shortest</th>"
		    "<TD "TD_BG" align=right colspan=2>%s bytes</td></TR>\n",
		    getRowColor(),
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.shortest.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);
      avgPktLen = (96*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo128.value
		   +192*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo256.value
		   +384*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo512.value
		   +768*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo1024.value
		   +1271*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo1518.value)/
	(myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value+1);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Average&nbsp;Size</th>"
		    "<TD "TD_BG" align=right colspan=2>%s bytes</td></TR>\n",
		    getRowColor(), formatPkts(avgPktLen, formatBuf, sizeof(formatBuf)));
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Longest</th>"
		    "<TD "TD_BG" align=right colspan=2>%s bytes</td></TR>\n",
		    getRowColor(), formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].
					      rcvdPktStats.longest.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Size&nbsp;&lt;=&nbsp;64&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdPktStats.upTo64.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo64.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">64&nbsp;&lt;&nbsp;Size&nbsp;&lt;=&nbsp;128&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdPktStats.upTo128.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo128.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">128&nbsp;&lt;&nbsp;Size&nbsp;&lt;=&nbsp;256&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdPktStats.upTo256.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo256.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">256&nbsp;&lt;&nbsp;Size&nbsp;&lt;=&nbsp;512&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdPktStats.upTo512.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo512.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">512&nbsp;&lt;&nbsp;Size&nbsp;&lt;=&nbsp;1024&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdPktStats.upTo1024.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo1024.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">1024&nbsp;&lt;&nbsp;Size&nbsp;&lt;=&nbsp;1518&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdPktStats.upTo1518.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo1518.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);

#ifdef MAKE_WITH_JUMBO_FRAMES
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">1518&nbsp;&lt;&nbsp;Size&nbsp;&lt;=&nbsp;2500&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdPktStats.upTo2500.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo2500.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">2500&nbsp;&lt;&nbsp;Size&nbsp;&lt;=&nbsp;6500&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdPktStats.upTo6500.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo6500.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">6500&nbsp;&lt;&nbsp;Size&nbsp;&lt;=&nbsp;9000&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdPktStats.upTo9000.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.upTo9000.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">&gt;&nbsp;9000&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdPktStats.above9000.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.above9000.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);
#else
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Size&nbsp;&gt;&nbsp;1518&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdPktStats.above1518.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.above1518.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);
#endif /* MAKE_WITH_JUMBO_FRAMES */

#ifndef EMBEDDED
      if(myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value > 0)
	sendString("<TR "TR_ON" BGCOLOR=white><TH "TH_BG" ALIGN=CENTER COLSPAN=3 BGCOLOR=white>"
		   "<iframe frameborder=0 SRC=\"" CONST_PIE_PKT_SIZE_DIST  CHART_FORMAT "\" width=400 height=250></iframe></TH></TR>\n");
#endif

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Packets&nbsp;too&nbsp;long [> %d]</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), myGlobals.mtuSize[myGlobals.device[myGlobals.actualReportDeviceId].datalink],
		    (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.tooLong.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.tooLong.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Bad&nbsp;Packets&nbsp;(Checksum)</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdPktStats.badChecksum.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktStats.badChecksum.value, formatBuf, sizeof(formatBuf)));
      sendString(buf);
    }

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

    if(!myGlobals.runningPref.printIpOnly &&
       myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value > 0) {
      sendString("</TABLE>"TABLE_OFF"</TR><TR><TH "TH_BG" ALIGN=LEFT "DARK_BG">FC Packets</TH><TD "TH_BG">\n<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"100%\">");

      if(myGlobals.device[myGlobals.actualReportDeviceId].pcapPtr != NULL) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Total</th>"
		      "<TD "TD_BG" align=right COLSPAN=2>%s [%s Pkts]</td></TR>\n",
		      getRowColor(),
		      formatBytes(myGlobals.device[myGlobals.actualReportDeviceId].fcBytes.value, 1,
				  formatBuf, sizeof(formatBuf)),
		      formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
				 formatBuf1, sizeof(formatBuf1)));
	sendString(buf);
#ifdef NOT_YET
	if(myGlobals.device[myGlobals.actualReportDeviceId].droppedPkts.value > 0) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			"<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Dropped&nbsp;by&nbsp;the&nbsp;kernel</th>"
			"<TD "TD_BG" COLSPAN=2 align=right>%s [%.2f %%]</td></TR>\n",
			getRowColor(),
			formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].droppedPkts.value,
				   formatBuf, sizeof(formatBuf)),
			(float)(myGlobals.device[myGlobals.actualReportDeviceId].droppedPkts.value*100)
			/(float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value);
	  sendString(buf);
	}
#endif
      }

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Unicast</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*unicastPkts)/(float)myGlobals.device[myGlobals.actualReportDeviceId].
		    fcPkts.value,
		    formatPkts(unicastPkts, formatBuf, sizeof(formatBuf)));
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Broadcast</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].fcBroadcastPkts.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].fcBroadcastPkts.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Shortest</th>"
		    "<TD "TD_BG" align=right colspan=2>%s bytes</td></TR>\n",
		    getRowColor(),
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.shortest.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);

      avgPktLen = myGlobals.device[myGlobals.actualReportDeviceId].fcBytes.value/
	myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value;
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Average&nbsp;Size</th>"
		    "<TD "TD_BG" align=right colspan=2>%s bytes</td></TR>\n",
		    getRowColor(), formatPkts(avgPktLen, formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Longest</th>"
		    "<TD "TD_BG" align=right colspan=2>%s bytes</td></TR>\n",
		    getRowColor(), formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].
					      rcvdFcPktStats.longest.value, formatBuf,
					      sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">&le&nbsp;36&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdFcPktStats.upTo36.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.upTo36.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" "DARK_BG" align=left>&le;&nbsp;48&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdFcPktStats.upTo48.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.upTo48.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" "DARK_BG" align=left>&le;&nbsp;52&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdFcPktStats.upTo52.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.upTo52.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" "DARK_BG" align=left>&le;&nbsp;68&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdFcPktStats.upTo68.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.upTo68.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" "DARK_BG" align=left>&le;&nbsp;104&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdFcPktStats.upTo104.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.upTo104.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" "DARK_BG" align=left>&le;&nbsp;548&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdFcPktStats.upTo548.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.upTo548.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" "DARK_BG" align=left>&le;&nbsp;1060&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdFcPktStats.upTo1060.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.upTo1060.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" "DARK_BG" align=left>&le;&nbsp;2136&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdFcPktStats.upTo2136.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.upTo2136.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" "DARK_BG" align=left>&gt;&nbsp;2136&nbsp;bytes</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdFcPktStats.above2136.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.above2136.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);

	sendString("<TR "TR_ON" BGCOLOR=white><TH "TH_BG" ALIGN=CENTER COLSPAN=3 BGCOLOR=white>"
		   "<iframe frameborder=0 SRC=\"" CONST_PIE_FC_PKT_SZ_DIST  CHART_FORMAT "\" width=400 height=250></iframe></TH></TR>\n");

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" "DARK_BG" align=left>Packets&nbsp;too&nbsp;long [> %d]</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), 2136,
		    (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.above2136.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.above2136.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" "DARK_BG" align=left>Bad&nbsp;EOF&nbsp;Frames</th>"
		    "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		    getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					   rcvdFcPktStats.badCRC.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].fcPkts.value,
		    formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdFcPktStats.badCRC.value,
			       formatBuf, sizeof(formatBuf)));
      sendString(buf);

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

    sendString("</TABLE>"TABLE_OFF"</TR><TR><TH "TH_BG" ALIGN=LEFT "DARK_BG">Traffic</TH><TD "TH_BG">\n<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"100%\">");
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Total</th>"
		  "<TD "TD_BG" align=right COLSPAN=2>%s [%s Pkts]</td></TR>\n",
		  getRowColor(),
		  formatBytes(myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value, 1, formatBuf, sizeof(formatBuf)),
		  formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">IP Traffic</th>"
		  "<TD "TD_BG" align=right COLSPAN=2>%s [%s Pkts]</td></TR>\n",
		  getRowColor(), formatBytes(myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value, 1, formatBuf, sizeof(formatBuf)),
		  formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].ipPkts.value, formatBuf1, sizeof(formatBuf1)));
    sendString(buf);

    if(myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value > 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Fragmented IP Traffic</th>"
		    "<TD "TD_BG" align=right COLSPAN=2>%s [%.1f%%]</td></TR>\n",
		    getRowColor(),
		    formatBytes(myGlobals.device[myGlobals.actualReportDeviceId].fragmentedIpBytes.value, 1, formatBuf, sizeof(formatBuf)),
		    (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].fragmentedIpBytes.value)/
		    (float)myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value);
      sendString(buf);
    }

    /* Just in case... */
    if(myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value >
       myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value)
      dummyCounter = myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value-
	myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value;
    else
      dummyCounter = 0;

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Non IP Traffic</th>"
		  "<TD "TD_BG" align=right COLSPAN=2>%s</td></TR>\n",
		  getRowColor(), formatBytes(dummyCounter, 1, formatBuf, sizeof(formatBuf)));
    sendString(buf);

#ifndef EMBEDDED
    if(myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value > 0)
	sendString("<TR "TR_ON" BGCOLOR=white><TH "TH_BG" ALIGN=CENTER COLSPAN=3 BGCOLOR=white>"
		   "<iframe frameborder=0 SRC=\"" CONST_PIE_IP_TRAFFIC  CHART_FORMAT "\" width=400 height=250></iframe></TH></TR>\n");
#endif

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

    if(myGlobals.device[myGlobals.actualReportDeviceId].ipPkts.value > 0) {
      int avgPktTTL;

      avgPktTTL = (int)((16*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo32.value
			 +48*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo64.value
			 +80*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo96.value
			 +112*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo128.value
			 +144*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo160.value
			 +176*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo192.value
			 +208*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo224.value
			 +240*myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo255.value)/
			myGlobals.device[myGlobals.actualReportDeviceId].ipPkts.value);

      if(avgPktTTL > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Average&nbsp;TTL</th>"
		      "<TD "TD_BG" align=right COLSPAN=2>%d</td></TR>\n",
		      getRowColor(), avgPktTTL);
	sendString(buf);
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">TTL &lt;= 32</th>"
		      "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		      getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					     rcvdPktTTLStats.upTo32.value)/
		      (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		      formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo32.value,
				 formatBuf, sizeof(formatBuf)));
	sendString(buf);
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">32 &lt; TTL &lt;= 64</th>"
		      "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		      getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					     rcvdPktTTLStats.upTo64.value)/
		      (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		      formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo64.value,
				 formatBuf, sizeof(formatBuf)));
	sendString(buf);
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">64 &lt; TTL &lt;= 96</th>"
		      "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		      getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					     rcvdPktTTLStats.upTo96.value)/
		      (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		      formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo96.value,
				 formatBuf, sizeof(formatBuf)));
	sendString(buf);
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">96 &lt; TTL &lt;= 128</th>"
		      "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		      getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					     rcvdPktTTLStats.upTo128.value)/
		      (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		      formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo128.value,
				 formatBuf, sizeof(formatBuf)));
	sendString(buf);
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">128 &lt; TTL &lt;= 160</th>"
		      "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		      getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					     rcvdPktTTLStats.upTo160.value)/
		      (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		      formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo160.value,
				 formatBuf, sizeof(formatBuf)));
	sendString(buf);
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">160 &lt; TTL &lt;= 192</th>"
		      "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		      getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					     rcvdPktTTLStats.upTo192.value)/
		      (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		      formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo192.value,
				 formatBuf, sizeof(formatBuf)));
	sendString(buf);
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">192 &lt; TTL &lt;= 224</th>"
		      "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		      getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					     rcvdPktTTLStats.upTo224.value)/
		      (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		      formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo224.value,
				 formatBuf, sizeof(formatBuf)));
	sendString(buf);
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">224 &lt; TTL &lt;= 256</th>"
		      "<TD "TD_BG" align=right>%.1f%%</td><TD "TD_BG" align=right>%s</td></TR>\n",
		      getRowColor(), (float)(100*myGlobals.device[myGlobals.actualReportDeviceId].
					     rcvdPktTTLStats.upTo255.value)/
		      (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value,
		      formatPkts(myGlobals.device[myGlobals.actualReportDeviceId].rcvdPktTTLStats.upTo255.value,
				 formatBuf, sizeof(formatBuf)));
	sendString(buf);

#ifndef EMBEDDED
	sendString("<TR "TR_ON" BGCOLOR=white><TH "TH_BG" ALIGN=CENTER COLSPAN=3 BGCOLOR=white>"
		   "<iframe frameborder=0 SRC=\"" CONST_PIE_TTL_DIST  CHART_FORMAT "\" width=400 height=250></iframe></TH></TR>\n");
#endif
      }
    }

    sendString("</TABLE>"TABLE_OFF"</TR>");

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

#ifndef EMBEDDED
    if(myGlobals.runningPref.enableSessionHandling && drawHostsDistanceGraph(1))
      sendString("<TR><TH "TH_BG" ALIGN=LEFT "DARK_BG">Remote Hosts Distance</TH>"
                 "<TD BGCOLOR=white ALIGN=CENTER>"
		 "<iframe frameborder=0 SRC=\"" CONST_BAR_HOST_DISTANCE CHART_FORMAT "\" width=400 height=250></iframe>"
                 "</TD></TR>\n");
#endif

    if(!myGlobals.device[myGlobals.actualReportDeviceId].dummyDevice) {
      updateThpt(0);

      sendString("<TR><TH "TH_BG" ALIGN=LEFT "DARK_BG">Network Load</TH><TD "TH_BG">\n<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"100%\">");
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Actual</th><TD "TD_BG" align=right>%s</td>"
		    "<TD "TD_BG" align=right>%.1f&nbsp;Pkt/s</td></TR>\n",
		    getRowColor(), formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].actualThpt,
						    1, formatBuf, sizeof(formatBuf)),
		    myGlobals.device[myGlobals.actualReportDeviceId].actualPktsThpt);
      sendString(buf);
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Last Minute</th>"
		    "<TD "TD_BG" align=right>%s</td>"
		    "<TD "TD_BG" align=right>%.1f&nbsp;Pkt/s</td></TR>\n",
		    getRowColor(), formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].lastMinThpt,
						    1, formatBuf, sizeof(formatBuf)),
		    myGlobals.device[myGlobals.actualReportDeviceId].lastMinPktsThpt);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Last 5 Minutes</th>"
		    "<TD "TD_BG" align=right>%s</td>"
		    "<TD "TD_BG" align=right>%.1f&nbsp;Pkt/s</td></TR>\n",
		    getRowColor(), formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].lastFiveMinsThpt,
						    1, formatBuf, sizeof(formatBuf)),
		    myGlobals.device[myGlobals.actualReportDeviceId].lastFiveMinsPktsThpt);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Peak</th>"
		    "<TD "TD_BG" align=right>%s</td>"
		    "<TD "TD_BG" align=right>%.1f&nbsp;Pkt/s</td></TR>\n",
		    getRowColor(), formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].peakThroughput,
						    1, formatBuf, sizeof(formatBuf)),
		    myGlobals.device[myGlobals.actualReportDeviceId].peakPacketThroughput);
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" align=left "DARK_BG">Average</th>"
		    "<TD "TD_BG" align=right>%s</td>"
		    "<TD "TD_BG" align=right>%.1f&nbsp;Pkt/s</td></TR>\n",
		    getRowColor(),
		    formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value/
				     (myGlobals.actTime-myGlobals.initialSniffTime+1), 1, formatBuf, sizeof(formatBuf)),
		    /* Bug below fixed courtesy of Eddy Lai <eddy@ModernTerminals.com> */
		    ((float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value/
		     (float)(myGlobals.actTime-myGlobals.initialSniffTime+1)));
      sendString(buf);

      
#if 0
      sendString("<script>if(hasSVGSupport) {\n");
      sendString("document.write(\"<TR "TR_ON" BGCOLOR=white><TH BGCOLOR=white ALIGN=CENTER COLSPAN=3>"
		  "<embed src=\"/graph_if.svg\" width=\"400\" height=\"250\" type=\"image/svg+xml\" />"
		  "</TH></TR>\n\"); }</script>\n");
 #endif

 #if 0
     sendString("<TR "TR_ON" BGCOLOR=white><TH BGCOLOR=white ALIGN=CENTER COLSPAN=3>"
		"<embed src=\"/bar.html\" width=\"400\" height=\"250\" SCROLLING=NO>"
		"</TH></TR>\n");

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

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

 #ifndef EMBEDDED
  if(strcmp(myGlobals.device[0].name, "pcap-file")) {
    /* RRD */
    /* Do NOT add a '/' at the end of the path because Win32 will complain about it */
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s/interfaces/%s",
		  myGlobals.rrdPath != NULL ? myGlobals.rrdPath : ".",
		  myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName);

    revertSlashIfWIN32(buf, 0);

    if((i = stat(buf, &statbuf)) == 0) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		    "<TR %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">Historical Data</TH>\n"
		    "<TD "TD_BG" align=\"right\">"
		    "[ <a href=\"/" CONST_PLUGINS_HEADER
		    "rrdPlugin?action=list&amp;key=interfaces/%s&amp;title=interface%%20%s\">"
		    "<img class=tooltip valign=\"top\" border=\"0\" src=\"/graph.gif\""
		    " alt=\"View rrd charts of historical data for this interface\"></a> ]"
		    "</TD></TR>\n",
		    getRowColor(), myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName,
		    myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName);
      sendString(buf);
    }
  }
 #endif
   /* ********************* */

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

   if(!myGlobals.runningPref.printFcOnly) {
     printProtoTraffic(TRUE);
     sendString("<p>\n");
     printIpProtocolDistribution(FLAG_HOSTLINK_HTML_FORMAT, revertOrder, TRUE);
   }
   if(!myGlobals.runningPref.printIpOnly) {
     sendString("<p>\n");
     printFcProtocolDistribution(FLAG_HOSTLINK_HTML_FORMAT, revertOrder, TRUE);
   }
 }

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

 int combineReportTypeLocality(int reportTypeReq, LocalityDisplayPolicy showLocalityMode) {
   int rc = reportTypeReq;

   switch(reportTypeReq) {
   case SORT_DATA_HOST_TRAFFIC:
     switch(showLocalityMode) {
     case showOnlySent:
       rc = SORT_DATA_SENT_HOST_TRAFFIC;
       break;
     case showOnlyReceived:
       rc = SORT_DATA_RCVD_HOST_TRAFFIC;
     default:
       /* Nothign to do */
       break;
     }
     break;
   case SORT_DATA_PROTOS:
     switch(showLocalityMode) {
     case showOnlySent:
       rc = SORT_DATA_SENT_PROTOS;
       break;
     case showOnlyReceived:
       rc = SORT_DATA_RECEIVED_PROTOS;
     default:
       /* Nothign to do */
       break;
     }
     break;
   case SORT_DATA_IP:
     switch(showLocalityMode) {
     case showOnlySent:
       rc = SORT_DATA_SENT_IP;
       break;
     case showOnlyReceived:
       rc = SORT_DATA_RECEIVED_IP;
     default:
       /* Nothign to do */
       break;
     }
     break;
   case SORT_DATA_THPT:
     switch(showLocalityMode) {
     case showOnlySent:
       rc = SORT_DATA_SENT_THPT;
       break;
     case showOnlyReceived:
       rc = SORT_DATA_RECEIVED_THPT;
     default:
       /* Nothign to do */
       break;
     }
     break;

   default:
     /* Nothing to do */
     break;
   }

   return(rc);
 }

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

 void printHostsTraffic(int reportTypeReq,
			int sortedColumn,
			int revertOrder,
			int pageNum,
			char* url,
			HostsDisplayPolicy showHostsMode,
			LocalityDisplayPolicy showLocalityMode,
			int vlanId) {
   u_int idx, idx1, numEntries=0;
   int printedEntries=0, hourId, maxHosts;
   char theDate[8];
   struct tm t;
   HostTraffic *el;
   HostTraffic** tmpTable;
   char buf[LEN_GENERAL_WORK_BUFFER*2], hostLinkBuf[LEN_GENERAL_WORK_BUFFER];
   float sentPercent=0, rcvdPercent=0, totPercent=0;
   Counter totIpBytesSent=0, totIpBytesRcvd=0, totIpBytes=0;
   Counter totEthBytesSent=0, totEthBytesRcvd=0, totEthBytes=0;
   ProtocolsList *protoList;
   char formatBuf[32], formatBuf1[32], formatBuf2[32], formatBuf3[32],
     formatBuf4[32], formatBuf5[32], formatBuf6[32], formatBuf7[32],
     formatBuf8[32], formatBuf9[32];
   int reportType;
   u_char *vlanList;

   vlanList = calloc(1, MAX_VLAN); if(vlanList == NULL) return;
   vlanId = abs(vlanId);

   /* traceEvent(CONST_TRACE_INFO, "VLAN: %d", vlanId); */

   reportType = combineReportTypeLocality(reportTypeReq, showLocalityMode);

   memset(buf, 0, sizeof(buf));
   switch(reportType) {
   case SORT_DATA_RCVD_HOST_TRAFFIC:
   case SORT_DATA_SENT_HOST_TRAFFIC:
   case SORT_DATA_HOST_TRAFFIC:
     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Network Activity: ");
     break;
   case SORT_DATA_RECEIVED_PROTOS:
   case SORT_DATA_SENT_PROTOS:
   case SORT_DATA_PROTOS:
     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Network Traffic [All Protocols]: ");
     break;
   case SORT_DATA_RECEIVED_IP:
   case SORT_DATA_SENT_IP:
   case SORT_DATA_IP:
     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Network Traffic [TCP/IP]: ");
     break;
   case SORT_DATA_RECEIVED_THPT:
   case SORT_DATA_SENT_THPT:
   case SORT_DATA_THPT:
     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Network Throughput: ");
     break;
   default:
     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "?: ");
     break;
   }

   switch(showHostsMode) {
   case showAllHosts:
     strncat(buf, "All Hosts", (sizeof(buf) - strlen(buf) - 1));
     break;
   case showOnlyLocalHosts:
     strncat(buf, "Local Hosts", (sizeof(buf) - strlen(buf) - 1));
     break;
   case showOnlyRemoteHosts:
     strncat(buf, "Remote Hosts", (sizeof(buf) - strlen(buf) - 1));
     break;
   }

   switch(showLocalityMode) {
   case showSentReceived:
     strncat(buf, " - Data Sent+Received", (sizeof(buf) - strlen(buf) - 1));
     break;
   case showOnlySent:
     strncat(buf, " - Data Sent", (sizeof(buf) - strlen(buf) - 1));
     break;
   case showOnlyReceived:
     strncat(buf, " - Data Received", (sizeof(buf) - strlen(buf) - 1));
     break;
   }

   for(el=getFirstHost(myGlobals.actualReportDeviceId);
       el != NULL; el = getNextHost(myGlobals.actualReportDeviceId, el))
     if((el->vlanId > 0) && (el->vlanId < MAX_VLAN)) vlanList[el->vlanId] = 1;

   printHTMLheader(buf, NULL, 0);

   printHeader(reportTypeReq, revertOrder, abs(sortedColumn), showHostsMode,
	       showLocalityMode, (char*)vlanList, vlanId);

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

   maxHosts = myGlobals.device[myGlobals.actualReportDeviceId].hostsno;
   /* save ths as it can change */

   tmpTable = (HostTraffic**)mallocAndInitWithReportWarn(maxHosts*sizeof(HostTraffic*),
							 "printHostsTraffic");
   if(tmpTable == NULL) {
     free(vlanList);
     return;
   }

   for(el=getFirstHost(myGlobals.actualReportDeviceId);
       el != NULL; el = getNextHost(myGlobals.actualReportDeviceId, el)) {
     if(!isFcHost (el) && (broadcastHost(el) == 0)) {
       u_char addHost;

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

       if(((showLocalityMode == showOnlySent) && (el->bytesSent.value > 0))
	  || ((showLocalityMode == showOnlyReceived) && (el->bytesRcvd.value > 0))
	  || ((showLocalityMode == showSentReceived) && (el->bytesSent.value + el->bytesRcvd.value > 0))) {
	 if(((reportType == SORT_DATA_RECEIVED_IP)
	     || (reportType == SORT_DATA_SENT_IP)
	     || (reportType == SORT_DATA_IP))
	    && (el->hostNumIpAddress[0] == '\0')) {
	   continue;
	 }

	 addHost = 1;

	 switch(myGlobals.hostsDisplayPolicy) {
	 case showOnlyLocalHosts:
	   if(!subnetPseudoLocalHost(el)) addHost = 0;
	   break;
	 case showOnlyRemoteHosts:
	   if(subnetPseudoLocalHost(el)) addHost = 0;
	   break;
	 default:
	   /* Nothign to do */
	   break;
	 }

	 if(addHost) {
	   tmpTable[numEntries++] = el;
	   if(numEntries >= maxHosts)
	     break;
	 }
       }
     }
   } /* for */

   if(numEntries > 0) {
     /*
       The switch below is needed to:
       - sort data according to the selected column
       - 'recycle' (somebody would call this "code reuse") the cmpFctn function
     */

     if(sortedColumn == FLAG_HOST_DUMMY_IDX)
       myGlobals.columnSort = FLAG_HOST_DUMMY_IDX; /* Host name */
     else if(sortedColumn == FLAG_DOMAIN_DUMMY_IDX)
       myGlobals.columnSort = FLAG_DOMAIN_DUMMY_IDX; /* domain name */
     else
       myGlobals.columnSort = sortedColumn;

 #ifdef DEBUG
     traceEvent(CONST_TRACE_INFO, ">reportType=%d/sortedColumn=%d/myGlobals.columnSort=%d<",
		reportType, sortedColumn, myGlobals.columnSort);
 #endif

     myGlobals.reportKind = reportType;
     /* if(myGlobals.columnSort == 0) myGlobals.reportKind = 0;*/

     qsort(tmpTable, numEntries, sizeof(HostTraffic*), cmpFctn);

     switch(reportType) {
     case SORT_DATA_RECEIVED_PROTOS:
     case SORT_DATA_SENT_PROTOS:
       totEthBytesSent = totEthBytesRcvd = 0;

       for(idx=0; idx<numEntries; idx++) {
	 if(tmpTable[idx] != NULL) {
	   totEthBytesSent += tmpTable[idx]->bytesSent.value;
	   totEthBytesRcvd += tmpTable[idx]->bytesRcvd.value;
	 }
       }

       /* Avoid core dumps */
       if(totEthBytesSent == 0) totEthBytesSent = 1;
       if(totEthBytesRcvd == 0) totEthBytesRcvd = 1;
       break;
     case SORT_DATA_PROTOS:
       totEthBytes = 0;

       for(idx=0; idx<numEntries; idx++) {
	 if(tmpTable[idx] != NULL) {
	   totEthBytes += tmpTable[idx]->bytesSent.value +
	     tmpTable[idx]->bytesRcvd.value;
	 }
       }

       /* Avoid core dumps */
       if(totEthBytes == 0) totEthBytes = 1;
       break;
     case SORT_DATA_RECEIVED_IP:
     case SORT_DATA_SENT_IP:
       totIpBytesSent = totIpBytesRcvd = 0;

       for(idx=0; idx<numEntries; idx++) {
	 if(tmpTable[idx] != NULL) {
	   totIpBytesSent += tmpTable[idx]->ipBytesSent.value;
	   totIpBytesRcvd += tmpTable[idx]->ipBytesRcvd.value;
	 }
       }

       /* Avoid core dumps */
       if(totIpBytesSent == 0) totIpBytesSent = 1;
       if(totIpBytesRcvd == 0) totIpBytesRcvd = 1;
       break;
     case SORT_DATA_IP:
       totIpBytes = 0;

       for(idx=0; idx<numEntries; idx++) {
	 if(tmpTable[idx] != NULL) {
	   totIpBytes += tmpTable[idx]->ipBytesSent.value +
	     tmpTable[idx]->ipBytesRcvd.value;
	 }
       }

       /* Avoid core dumps */
       if(totIpBytes == 0) totIpBytes = 1;
       break;
     }

 #ifdef DEBUG
     traceEvent(CONST_TRACE_INFO, "totIpBytesSent=%u, totIpBytesRcvd=%u totIpBytes=%u",
		totIpBytesSent, totIpBytesRcvd, totIpBytes);
 #endif

     for(idx=pageNum*myGlobals.runningPref.maxNumLines; idx<numEntries; idx++) {
       int i;
       char webHostName[LEN_GENERAL_WORK_BUFFER];

       if(revertOrder)
	 el = tmpTable[numEntries-idx-1];
       else
	 el = tmpTable[idx];

       if(el != NULL) {
	 switch(reportType) {
	 case SORT_DATA_RECEIVED_PROTOS:
	 case SORT_DATA_SENT_PROTOS:
	   sentPercent = (100*(float)el->bytesSent.value)/totEthBytesSent;
	   rcvdPercent = (100*(float)el->bytesRcvd.value)/totEthBytesRcvd;
	   break;
	 case SORT_DATA_PROTOS:
	   totPercent = (100*(float) (el->bytesSent.value + el->bytesRcvd.value) )/totEthBytes;
	   break;
	 case SORT_DATA_RECEIVED_IP:
	 case SORT_DATA_SENT_IP:
	   sentPercent = (100*(float)el->ipBytesSent.value)/totIpBytesSent;
	   rcvdPercent = (100*(float)el->ipBytesRcvd.value)/totIpBytesRcvd;
	   break;
	 case SORT_DATA_IP:
	   totPercent = (100*(float) (el->ipBytesSent.value + el->ipBytesRcvd.value) )/totIpBytes;
	   break;
	 case SORT_DATA_RECEIVED_THPT:
	 case SORT_DATA_RCVD_HOST_TRAFFIC:
	 case SORT_DATA_SENT_HOST_TRAFFIC:
	 case SORT_DATA_SENT_THPT:
	 case TRAFFIC_STATS:
	 case SORT_DATA_HOST_TRAFFIC:
	 case SORT_DATA_THPT:
	   sentPercent = rcvdPercent = 0;
	   break;
	 }

	 /*
	   Fixed buffer overflow.
	   Courtesy of Rainer Tammer <rainer.tammer@spg.schulergroup.com>
	 */
	 strncpy(webHostName, makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 1,
					   hostLinkBuf, sizeof(hostLinkBuf)),
		 sizeof(webHostName));

	 switch(reportType) {
	 case SORT_DATA_RECEIVED_PROTOS:
	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>%s"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%s%%</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>""<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			 getRowColor(), webHostName,
			 formatBytes(el->bytesRcvd.value, 1, formatBuf, sizeof(formatBuf)),
			 rcvdPercent, myGlobals.separator,
			 formatBytes(el->tcpRcvdLoc.value+el->tcpRcvdFromRem.value, 1, formatBuf1, sizeof(formatBuf1)),
			 formatBytes(el->udpRcvdLoc.value+el->udpRcvdFromRem.value, 1, formatBuf2, sizeof(formatBuf2)),
			 formatBytes(el->icmpRcvd.value, 1, formatBuf3, sizeof(formatBuf3)),
			 formatBytes(el->icmp6Rcvd.value, 1, formatBuf4, sizeof(formatBuf4)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->dlcRcvd.value, 1, formatBuf5, sizeof(formatBuf5)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->ipxRcvd.value, 1, formatBuf6, sizeof(formatBuf6)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->decnetRcvd.value, 1, formatBuf7, sizeof(formatBuf7)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->arp_rarpRcvd.value, 1, formatBuf8, sizeof(formatBuf8)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->appletalkRcvd.value, 1, formatBuf9, sizeof(formatBuf9))
			 );
	   sendString(buf);

	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->netbiosRcvd.value, 1, formatBuf1, sizeof(formatBuf1)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->osiRcvd.value, 1, formatBuf2, sizeof(formatBuf2)),
			 formatBytes(el->ipv6Rcvd.value, 1, formatBuf3, sizeof(formatBuf3)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->stpRcvd.value, 1, formatBuf4, sizeof(formatBuf4)));
	   sendString(buf);

	   protoList = myGlobals.ipProtosList, idx1=0;
	   while(protoList != NULL) {
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			   el->ipProtosList[idx1] != NULL ?
			   formatBytes(el->ipProtosList[idx1]->rcvd.value, 1, formatBuf, sizeof(formatBuf)) : "0");
	     sendString(buf);

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

	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->otherRcvd.value, 1, formatBuf, sizeof(formatBuf)));
	   sendString(buf);
	   break;
	 case SORT_DATA_SENT_PROTOS:
	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>%s"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%s%%</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>""<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			 getRowColor(), webHostName,
			 formatBytes(el->bytesSent.value, 1, formatBuf, sizeof(formatBuf)), sentPercent, myGlobals.separator,
			 formatBytes(el->tcpSentLoc.value+el->tcpSentRem.value, 1, formatBuf1, sizeof(formatBuf1)),
			 formatBytes(el->udpSentLoc.value+el->udpSentRem.value, 1, formatBuf2, sizeof(formatBuf2)),
			 formatBytes(el->icmpSent.value, 1, formatBuf3, sizeof(formatBuf3)),
			 formatBytes(el->icmp6Sent.value, 1, formatBuf4, sizeof(formatBuf4)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->dlcSent.value, 1, formatBuf5, sizeof(formatBuf5)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->ipxSent.value, 1, formatBuf6, sizeof(formatBuf6)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->decnetSent.value, 1, formatBuf7, sizeof(formatBuf7)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->arp_rarpSent.value, 1, formatBuf8, sizeof(formatBuf8)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->appletalkSent.value, 1, formatBuf9, sizeof(formatBuf9))
			 );

	   sendString(buf);

	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->netbiosSent.value, 1, formatBuf, sizeof(formatBuf)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->osiSent.value, 1, formatBuf1, sizeof(formatBuf1)),
			 formatBytes(el->ipv6Sent.value, 1, formatBuf2, sizeof(formatBuf2)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->stpSent.value, 1, formatBuf3, sizeof(formatBuf3)));
	   sendString(buf);

	   protoList = myGlobals.ipProtosList, idx1=0;
	   while(protoList != NULL) {
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			   el->ipProtosList[idx1] != NULL ?
			   formatBytes(el->ipProtosList[idx1]->sent.value, 1,
				       formatBuf, sizeof(formatBuf)) : "0");
	     sendString(buf);

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

	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->otherSent.value, 1, formatBuf, sizeof(formatBuf))
			 );
	   sendString(buf);
	   break;
	 case SORT_DATA_PROTOS:
	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>%s"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%s%%</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>""<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			 getRowColor(), webHostName,
			 formatBytes(el->bytesSent.value+el->bytesRcvd.value, 1, formatBuf, sizeof(formatBuf)),
			 totPercent, myGlobals.separator,
			 formatBytes(el->tcpSentLoc.value+el->tcpSentRem.value+
				     el->tcpRcvdLoc.value+el->tcpRcvdFromRem.value, 1, formatBuf1, sizeof(formatBuf1)),
			 formatBytes(el->udpSentLoc.value+el->udpSentRem.value+
				     el->udpRcvdLoc.value+el->udpRcvdFromRem.value, 1, formatBuf2, sizeof(formatBuf2)),
			 formatBytes(el->icmpSent.value+el->icmpRcvd.value, 1, formatBuf3, sizeof(formatBuf3)),
			 formatBytes(el->icmp6Sent.value+el->icmp6Rcvd.value, 1, formatBuf4, sizeof(formatBuf4)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->dlcSent.value+el->nonIPTraffic->dlcRcvd.value,
				     1, formatBuf5, sizeof(formatBuf5)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->ipxSent.value+el->nonIPTraffic->ipxRcvd.value,
				     1, formatBuf6, sizeof(formatBuf6)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->decnetSent.value+el->nonIPTraffic->decnetRcvd.value,
				     1, formatBuf7, sizeof(formatBuf7)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->arp_rarpSent.value+el->nonIPTraffic->arp_rarpRcvd.value,
				     1, formatBuf8, sizeof(formatBuf8)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->appletalkSent.value+el->nonIPTraffic->appletalkRcvd.value,
				     1, formatBuf9, sizeof(formatBuf9))
			 );

	   sendString(buf);

	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->netbiosSent.value+el->nonIPTraffic->netbiosRcvd.value,
				     1, formatBuf, sizeof(formatBuf)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->osiSent.value+el->nonIPTraffic->osiRcvd.value,
				     1, formatBuf1, sizeof(formatBuf1)),
			 formatBytes(el->ipv6Sent.value+el->ipv6Rcvd.value, 1, formatBuf2, sizeof(formatBuf2)),
			 formatBytes(el->nonIPTraffic == NULL ? 0 : el->nonIPTraffic->stpSent.value+el->nonIPTraffic->stpRcvd.value,
				     1, formatBuf3, sizeof(formatBuf3)));
	   sendString(buf);

	   protoList = myGlobals.ipProtosList, idx1=0;
	   while(protoList != NULL) {
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			   el->ipProtosList[idx1] != NULL ?
			   formatBytes(el->ipProtosList[idx1]->sent.value
				       +el->ipProtosList[idx1]->rcvd.value, 1,
				       formatBuf, sizeof(formatBuf)) : "0");
	     sendString(buf);

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

	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			 formatBytes(el->nonIPTraffic == NULL ? 0 :
				     el->nonIPTraffic->otherSent.value+el->nonIPTraffic->otherRcvd.value,
				     1, formatBuf, sizeof(formatBuf))
			 );
	   sendString(buf);

	   break;
	 case SORT_DATA_RECEIVED_IP:
	   {
	     Counter totalIPTraffic=0;

	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>%s"
			   "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%s%%</TD>",
			   getRowColor(), webHostName,
			   formatBytes(el->ipBytesRcvd.value, 1, formatBuf, sizeof(formatBuf)),
			   rcvdPercent, myGlobals.separator);
	     sendString(buf);

	     for(i=0; i<myGlobals.numIpProtosToMonitor; i++) {
	       if(el->protoIPTrafficInfos[i])
		 totalIPTraffic += el->protoIPTrafficInfos[i]->rcvdLoc.value+
		   el->protoIPTrafficInfos[i]->rcvdFromRem.value;
	       safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			     el->protoIPTrafficInfos[i] ?
			     formatBytes(el->protoIPTrafficInfos[i]->rcvdLoc.value+
					 el->protoIPTrafficInfos[i]->rcvdFromRem.value, 1,
					 formatBuf, sizeof(formatBuf)) : "0");
	       sendString(buf);
	     }

	     /* Rounding may cause troubles */
	     if(el->ipBytesRcvd.value > totalIPTraffic)
	       totalIPTraffic = el->ipBytesRcvd.value - totalIPTraffic;
	     else
	       totalIPTraffic = 0;
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			   formatBytes(totalIPTraffic, 1, formatBuf, sizeof(formatBuf)));
	     sendString(buf);
	   }
	   break;
	 case SORT_DATA_SENT_IP:
	   {
	     Counter totalIPTraffic=0;

	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>%s"
			   "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%s%%</TD>",
			   getRowColor(), webHostName,
			   formatBytes(el->ipBytesSent.value, 1, formatBuf, sizeof(formatBuf)),
			   sentPercent, myGlobals.separator);
	     sendString(buf);

	     for(i=0; i<myGlobals.numIpProtosToMonitor; i++) {
	       if(el->protoIPTrafficInfos[i])
		 totalIPTraffic += el->protoIPTrafficInfos[i]->sentLoc.value+
		   el->protoIPTrafficInfos[i]->sentRem.value;

	       safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			     el->protoIPTrafficInfos[i] ?
			     formatBytes(el->protoIPTrafficInfos[i]->sentLoc.value+
					 el->protoIPTrafficInfos[i]->sentRem.value, 1,
					 formatBuf, sizeof(formatBuf)) : "0");
	       sendString(buf);
	     }

	     /* Rounding may cause troubles */
	     if(el->ipBytesSent.value > totalIPTraffic)
	       totalIPTraffic = el->ipBytesSent.value - totalIPTraffic;
	     else
	       totalIPTraffic = 0;
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			   formatBytes(totalIPTraffic, 1, formatBuf, sizeof(formatBuf)));
	     sendString(buf);
	   }
	   break;
	 case SORT_DATA_IP:
	   {
	     Counter totalIPTraffic=0;

	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>%s"
			   "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%s%%</TD>",
			   getRowColor(), webHostName,
			   formatBytes(el->ipBytesSent.value+el->ipBytesRcvd.value, 1, formatBuf, sizeof(formatBuf)),
			   totPercent, myGlobals.separator);
	     sendString(buf);

	     for(i=0; i<myGlobals.numIpProtosToMonitor; i++) {
	       if(el->protoIPTrafficInfos[i])
		 totalIPTraffic += el->protoIPTrafficInfos[i]->sentLoc.value+
		   el->protoIPTrafficInfos[i]->rcvdLoc.value+
		   el->protoIPTrafficInfos[i]->sentRem.value+
		   el->protoIPTrafficInfos[i]->rcvdFromRem.value;
	       safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			     el->protoIPTrafficInfos[i] ?
			     formatBytes(el->protoIPTrafficInfos[i]->sentLoc.value+
					 el->protoIPTrafficInfos[i]->rcvdLoc.value+
					 el->protoIPTrafficInfos[i]->sentRem.value+
					 el->protoIPTrafficInfos[i]->rcvdFromRem.value, 1,
					 formatBuf, sizeof(formatBuf)) : "0");
	       sendString(buf);
	     }

	     /* Rounding may cause troubles */
	     if(el->ipBytesSent.value+el->ipBytesRcvd.value > totalIPTraffic)
	       totalIPTraffic = el->ipBytesSent.value + el->ipBytesRcvd.value - totalIPTraffic;
	     else
	       totalIPTraffic = 0;
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>",
			   formatBytes(totalIPTraffic, 1, formatBuf, sizeof(formatBuf)));
	     sendString(buf);
	   }
	   break;
	 case SORT_DATA_RECEIVED_THPT:
	   {
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>%s"
			   "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>",
			   getRowColor(), webHostName,
			   formatThroughput(el->actualRcvdThpt, 1, formatBuf, sizeof(formatBuf)),
			   formatThroughput(el->averageRcvdThpt, 1, formatBuf1, sizeof(formatBuf1)),
			   formatThroughput(el->peakRcvdThpt, 1, formatBuf2, sizeof(formatBuf2)),
			   el->actualRcvdPktThpt,
			   el->averageRcvdPktThpt,
			   el->peakRcvdPktThpt);
	     sendString(buf);
	   }
	   break;
	 case SORT_DATA_SENT_THPT:
	   {
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>%s"
			   "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>",
			   getRowColor(), webHostName,
			   formatThroughput(el->actualSentThpt, 1, formatBuf, sizeof(formatBuf)),
			   formatThroughput(el->averageSentThpt, 1, formatBuf1, sizeof(formatBuf1)),
			   formatThroughput(el->peakSentThpt, 1, formatBuf2, sizeof(formatBuf2)),
			   el->actualSentPktThpt,
			   el->averageSentPktThpt,
			   el->peakSentPktThpt);
	     sendString(buf);
	   }
	   break;
	 case SORT_DATA_THPT:
	   {
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>%s"
			   "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>"
			   "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>",
			   getRowColor(), webHostName,
			   formatThroughput(el->actualTThpt, 1, formatBuf, sizeof(formatBuf)),
			   formatThroughput(el->averageTThpt, 1, formatBuf1, sizeof(formatBuf1)),
			   formatThroughput(el->peakTThpt, 1, formatBuf2, sizeof(formatBuf2)),
			   el->actualTPktThpt,
			   el->averageTPktThpt,
			   el->peakTPktThpt);
	     sendString(buf);
	   }
	   break;
	 case SORT_DATA_RCVD_HOST_TRAFFIC:
	 case SORT_DATA_SENT_HOST_TRAFFIC:
	 case SORT_DATA_HOST_TRAFFIC:
	 case TRAFFIC_STATS:
	   {
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR %s>%s", getRowColor(), webHostName);
	     sendString(buf);
	     printHostThtpShort(el, reportType, hourId);
	   }
	   break;
	 }

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

	 /* Avoid huge tables */
	 if(printedEntries++ > myGlobals.runningPref.maxNumLines)
	   break;
       }
     }
   } else
     idx = 0;

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

   switch(reportType) {
   case SORT_DATA_RCVD_HOST_TRAFFIC:
   case SORT_DATA_SENT_HOST_TRAFFIC:
   case SORT_DATA_HOST_TRAFFIC:
   case SORT_DATA_RECEIVED_THPT:
   case SORT_DATA_SENT_THPT:
   case SORT_DATA_THPT:
     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:
     sendString("<P><I>Note: These counters do not include broadcasts and will not equal the 'Global Protocol Distribution'</I></P>\n");
     break;
   }

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

   printFooter(reportType);

   addPageIndicator(url, pageNum, numEntries, myGlobals.runningPref.maxNumLines,
		    revertOrder, abs(sortedColumn), -1);

   sendString("<p><b>NOTE</b>:</p>\n<ul>"
	      "<li>Click <a href=\"" CONST_HOST_SORT_NOTE_HTML "\">here</a> "
	      "for more information about host and domain sorting.</li>\n"
	      "</ul><p>\n");

   myGlobals.lastRefreshTime = myGlobals.actTime;
   free(vlanList);
   free(tmpTable);
 }

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

 void printMulticastStats(int sortedColumn /* ignored so far */,
			  int revertOrder,
			  int pageNum) {
   u_int idx, numEntries=0, maxHosts;
   int printedEntries=0, i;
   HostTraffic *el;
   HostTraffic** tmpTable;
   char buf[LEN_GENERAL_WORK_BUFFER], *sign, *theAnchor[6], *arrow[6], *arrowGif;
   char formatBuf[32], formatBuf1[32], formatBuf2[32], formatBuf3[32];
   char htmlAnchor[64], htmlAnchor1[64], hostLinkBuf[LEN_GENERAL_WORK_BUFFER];

   printHTMLheader("Multicast Statistics", NULL, 0);

   memset(buf, 0, sizeof(buf));
   maxHosts = myGlobals.device[myGlobals.actualReportDeviceId].hostsno; /* save it as it can change */

   tmpTable = (HostTraffic**)mallocAndInitWithReportWarn(maxHosts*sizeof(HostTraffic*), "printMulticastStats");
   if(tmpTable == NULL)
     return;

   /* All the ALT tags courtesy of "Burton M. Strauss III" <BStrauss3@attbi.com> */
   if(revertOrder) {
     sign = "";
     arrowGif = "&nbsp;" CONST_IMG_ARROW_UP;
   } else {
     sign = "-";
     arrowGif = "&nbsp;" CONST_IMG_ARROW_DOWN;
   }

   for(el=getFirstHost(myGlobals.actualReportDeviceId);
       el != NULL; el = getNextHost(myGlobals.actualReportDeviceId, el)) {
     if(((el->pktMulticastSent.value > 0) || (el->pktMulticastRcvd.value > 0))
	&& (!broadcastHost(el))) {
       if(el->community && (!isAllowedCommunity(el->community))) continue;
       tmpTable[numEntries++] = el;
     }

     if(numEntries >= maxHosts)
       break;
   }

   if(numEntries > 0) {
     myGlobals.columnSort = sortedColumn; /* Host name */

     safe_snprintf(__FILE__, __LINE__, htmlAnchor, sizeof(htmlAnchor), "<A HREF=/%s?col=%s", CONST_MULTICAST_STATS_HTML, sign);
     safe_snprintf(__FILE__, __LINE__, htmlAnchor1, sizeof(htmlAnchor1), "<A HREF=/%s?col=", CONST_MULTICAST_STATS_HTML);

     for(i=0; i<=5; i++)
       if(abs(myGlobals.columnSort) == i)
	 arrow[i] = arrowGif, theAnchor[i] = htmlAnchor;
       else
	 arrow[i] = "", theAnchor[i] = htmlAnchor1;

     sendString("<CENTER>\n");
     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		   ""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "TR_ON" "DARK_BG"><TH "TH_BG">%s0>Host%s</A></TH>\n"
		   "<TH "TH_BG">%s1>Domain%s</A></TH>"
		   "<TH "TH_BG">%s2>Pkts Sent%s</A></TH>"
		   "<TH "TH_BG">%s3>Data Sent%s</A></TH>"
		   "<TH "TH_BG">%s4>Pkts Rcvd%s</A></TH>"
		   "<TH "TH_BG">%s5>Data Rcvd%s</A></TH>"
		   "</TR>\n",
		   theAnchor[0], arrow[0],
		   theAnchor[1], arrow[1],
		   theAnchor[2], arrow[2],
		   theAnchor[3], arrow[3],
		   theAnchor[4], arrow[4],
		   theAnchor[5], arrow[5]
		   );
     sendString(buf);

     qsort(tmpTable, numEntries, sizeof(HostTraffic*), cmpMulticastFctn);

     for(idx=pageNum*myGlobals.runningPref.maxNumLines; idx<numEntries; idx++) {
       if(revertOrder)
	 el = tmpTable[numEntries-idx-1];
       else
	 el = tmpTable[idx];

       if(el != NULL) {
	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>%s"
		       "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		       "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		       "</TR>\n",
		       getRowColor(), makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 1, hostLinkBuf, sizeof(hostLinkBuf)),
		       formatPkts(el->pktMulticastSent.value, formatBuf, sizeof(formatBuf)),
		       formatBytes(el->bytesMulticastSent.value, 1, formatBuf1, sizeof(formatBuf1)),
		       formatPkts(el->pktMulticastRcvd.value, formatBuf2, sizeof(formatBuf2)),
		       formatBytes(el->bytesMulticastRcvd.value, 1, formatBuf3, sizeof(formatBuf3)));

	 sendString(buf);

	 /* Avoid huge tables */
	 if(printedEntries++ > myGlobals.runningPref.maxNumLines)
	   break;
       }
     }

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

     addPageIndicator(CONST_MULTICAST_STATS_HTML, pageNum, numEntries, myGlobals.runningPref.maxNumLines,
		      revertOrder, abs(sortedColumn), -1);

     printFooterHostLink();

   } else
     printNoDataYet();

   free(tmpTable);

   sendString("<P ALIGN=right><A class=external HREF=http://www.iana.org/assignments/multicast-addresses>List</A> of multicast addresses</P>\n");
 }

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

static void makeHostName(HostTraffic *el, char *buf, int len) {
  if(el->hostResolvedName[0] != '\0') strcpy(buf, el->hostResolvedName);
  else if(el->hostNumIpAddress[0] != '\0') strcpy(buf, el->hostNumIpAddress);
  else if(el->ethAddressString[0] != '\0') strcpy(buf, el->ethAddressString);
}

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

 #define LOCAL_COLOR     "mistyrose2"
 #define REMOTE_COLOR    "lightsteelblue1"

 static int addNodeInfo(FILE *fd, HostTraffic *el) {
   char buf0[128], buf1[128];

   makeHostName(el, buf0, sizeof(buf0));
   makeHostLink(el, FLAG_HOSTLINK_TEXT_LITE_FORMAT, 0, 0, buf1, sizeof(buf1));
   if(buf1[0] != '\0') {
     fprintf(fd, "\"%s\" [URL=\"%s\", color=%s];\n", buf0, buf1,
	     subnetLocalHost(el) ? LOCAL_COLOR : REMOTE_COLOR);
     return(1);
   }
   return(0);
 }

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

 void makeDot() {
 #ifdef WIN32
   returnHTTPpageNotFound("<b>This feature is not available on your platform</b>");
   return;
 #else
   HostTraffic *el, *el2, tmpEl;
   char buf[LEN_GENERAL_WORK_BUFFER], buf1[LEN_GENERAL_WORK_BUFFER],
     path[384], dotPath[256];
   FILE *fd, *cmap, *in, *out, *make;
   struct stat statbuf;
   int rc;

   printHTMLheader("Local Network Traffic Map", NULL, 0);

   /*
     First of all let's see if the path of dot is inside
     the preferences
   */
   if(fetchPrefsValue("dot.path", buf, sizeof(buf)) != -1) {
     snprintf(dotPath, sizeof(dotPath), "%s", buf);
   } else {
     snprintf(dotPath, sizeof(dotPath), "/usr/local/bin/dot");
     storePrefsValue("dot.path", dotPath); /* Set the default */
   }

   revertSlashIfWIN32(dotPath, 0);

   if(stat(dotPath, &statbuf) != 0) {
     snprintf(buf, sizeof(buf),
	      "<h1>ERROR</h1>\n<center><b>Missing <A HREF=http://www.graphviz.org/>dot</A> tool (expected %s). Please set its path (key dot.path) "
	      "<A HREF="CONST_EDIT_PREFS">here</A>.</b></center>",
	      dotPath);
     sendString(buf);
     return;
   }

   snprintf(path, sizeof(path), "%s/ntop-all.dot", myGlobals.spoolPath);
   fd = fopen(path, "w");

   if(fd != NULL) {
     for(el=getFirstHost(myGlobals.actualReportDeviceId);
	 el != NULL; el = getNextHost(myGlobals.actualReportDeviceId, el)) {
       int numEntries, i, urlSent = 0;

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

       if(subnetLocalHost(el)) {
	 makeHostName(el, buf, sizeof(buf));

	 for(numEntries = 0, i=0; i<MAX_NUM_CONTACTED_PEERS; i++)
	   if(!emptySerial(&el->contactedSentPeers.peersSerials[i])
	      && (!cmpSerial(&el->contactedSentPeers.peersSerials[i], &myGlobals.otherHostEntry->hostSerial))) {
	     if((el2 = quickHostLink(el->contactedSentPeers.peersSerials[i], myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {

	       makeHostName(el2, buf1, sizeof(buf1));
	       if(addNodeInfo(fd, el2)) {
		 fprintf(fd, "\"%s\" -> \"%s\";\n", buf, buf1);
		 if(!urlSent) urlSent = addNodeInfo(fd, el);
	       }
	     }
	   }

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

	 for(numEntries = 0, i=0; i<MAX_NUM_CONTACTED_PEERS; i++)
	   if(!emptySerial(&el->contactedRcvdPeers.peersSerials[i])
	      && (!cmpSerial(&el->contactedRcvdPeers.peersSerials[i], &myGlobals.otherHostEntry->hostSerial))) {
	     if((el2 = quickHostLink(el->contactedRcvdPeers.peersSerials[i], myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	       makeHostName(el2, buf1, sizeof(buf1));

	       for(i=0; i<strlen(buf1); i++) if(buf1[i] == '\"') buf1[i] = ' ';

	       if(addNodeInfo(fd, el2)) {
		 fprintf(fd, "\"%s\" -> \"%s\";\n", buf1, buf);
		 if(!urlSent) urlSent = addNodeInfo(fd, el);
	       }
	     }
	   }
       }
     }

     fclose(fd);

     snprintf(path, sizeof(path), "sort -u %s/ntop-all.dot > %s/ntop-sort.dot", myGlobals.spoolPath, myGlobals.spoolPath);
     sendString("<!-- sort command is ");
     sendString(path);
     sendString(" -->\n");
     errno = 0;
     rc = system(path);
     if((rc == -1) && (errno != ECHILD)) {
       snprintf(buf, sizeof(buf),
		"<h1>ERROR</h1>\n<center><b>Sorting of ntop-all.dot failed, rc %d</b></center>",
		errno);
       sendString(buf);
       return;
     }

     snprintf(path, sizeof(path), "%s/ntop.dot", myGlobals.spoolPath);
     out = fopen(path, "w");

     if(out != NULL) {
       fprintf(out, "digraph ntop {\n");
       fprintf(out, "node [shape = polygon, sides=4, fontsize=9, style=filled, fontname=\"Helvetica\"];\n");

       snprintf(path, sizeof(path), "%s/ntop-sort.dot", myGlobals.spoolPath);
       if((in = fopen(path, "r")) != NULL) {
	 while(!feof(in) && (fgets(buf, sizeof(buf), in) != NULL))
	   fprintf(out, "%s", buf);
       }

       fprintf(out, "}\n");
       fclose(out);
       fclose(in);
     }

     /* Added -c courtesy of Max Waterman <davidmaxwaterman@fastmail.co.uk> */
     snprintf(path, sizeof(path), "%s -Tpng -Goverlap=false %s/ntop.dot -o %s/"CONST_NETWORK_IMAGE_MAP " 2>&1 ",
	      dotPath, myGlobals.spoolPath, myGlobals.spoolPath);
     sendString("<!-- dot(generate) command is ");
     sendString(path);
     sendString(" -->\n");
     errno = 0;
     make = popen(path, "r");
     if(make == NULL) {
       snprintf(buf, sizeof(buf),
		"<h1>ERROR</h1>\n<center><p>Creation of network map failed, rc %s(%d)</p></center>\n"
		"<p>Command was:</p>\n<pre>%s</pre>",
		strerror(errno), errno, path);
       sendString(buf);
       return;
     }
     if(!feof(make) && (fgets(buf, sizeof(buf), make) != NULL)) {
       sendString("<h1>ERROR</h1>\n<center><p>Creation of network map failed</p></center>\n"
		  "<p>Command was:</p>\n<pre>");
       sendString(path);
       sendString("</pre>\n<p>Results were:</p>\n<pre>");
       sendString(buf);
       while(!feof(make) && (fgets(buf, sizeof(buf), make) != NULL)) {
	 sendString(buf);
       }
       sendString("</pre>\n");
       return;
     }
     pclose(make);

     snprintf(path, sizeof(path), "%s -Tcmap -Goverlap=false %s/ntop.dot", dotPath, myGlobals.spoolPath);
     sendString("<!-- dot(cmap) command is ");
     sendString(path);
     sendString(" -->\n");
     cmap = popen(path, "r");

     if(cmap != NULL) {
       sendString("<p><center><img src=\"/"CONST_NETWORK_IMAGE_MAP"\" usemap=\"#G\" ismap=\"ismap\" border=\"0\">");
       sendString("</center><map id=\"G\" name=\"G\">\n");

       while(!feof(cmap) && (fgets(buf, sizeof(buf), cmap) != NULL))
	 sendString(buf);

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

       sendString("<p><small>Graph generated by Dot, part of <A class=external HREF=http://www.graphviz.org>Graphviz</A>, created by "
		  "<A HREF=http://www.research.att.com/>AT&T Research</A>.</small>\n");

       pclose(cmap);
     } else {
       returnHTTPpageNotFound("Unable to generate cmap file (Is dot installed?)");
     }
   } else {
     returnHTTPpageNotFound("Unable to create temporary file");
   }
 #endif
 }

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

 void printHostsInfo(int sortedColumn, int revertOrder, int pageNum, int showBytes, int vlanId, int ifId) {
   u_int idx, numEntries=0, maxHosts;
   int printedEntries=0;
   unsigned short maxBandwidthUsage=1 /* avoid divisions by zero */;
   HostTraffic *el;
   HostTraffic** tmpTable;
   char buf[2*LEN_GENERAL_WORK_BUFFER], *arrowGif, *sign, *arrow[12], *theAnchor[12], osBuf[160];
   char htmlAnchor[64], htmlAnchor1[64];
   char formatBuf[32], hostLinkBuf[LEN_GENERAL_WORK_BUFFER];
   u_char *vlanList, foundVlan = 0, vlanStr[16], ifStr[16], foundIf = 0, *ifList;

   vlanList = calloc(1, MAX_VLAN);
   if(vlanList == NULL) {
     traceEvent (CONST_TRACE_WARNING, "Unable to allocate memory for vlan list");
     return;
   }
   vlanId = abs(vlanId);

   ifList = calloc(1, MAX_INTERFACE);
   if(ifList == NULL) {
     traceEvent (CONST_TRACE_WARNING, "Unable to allocate memory for if list");
     return;
   }
   ifId = abs(ifId);

   printHTMLheader("Host Information", NULL, 0);

   memset(buf, 0, sizeof(buf));
   maxHosts = myGlobals.device[myGlobals.actualReportDeviceId].hostsno; /* save it as it can change */

   tmpTable = (HostTraffic**)mallocAndInitWithReportWarn(maxHosts*sizeof(HostTraffic*), "printHostsInfo");
   if(tmpTable == NULL) {
     free(vlanList); free(ifList);
     return;
   }

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

   myGlobals.columnSort = sortedColumn;

   for(el=getFirstHost(myGlobals.actualReportDeviceId);
       el != NULL; el = getNextHost(myGlobals.actualReportDeviceId, el)) {
     unsigned short actUsage, actUsageS, actUsageR;

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

     if((el->vlanId != NO_VLAN) && (el->vlanId < MAX_VLAN))       { vlanList[el->vlanId] = 1, foundVlan = 1; }
     if((vlanId != NO_VLAN) && (el->vlanId != vlanId)) continue;

     if((el->ifId != NO_INTERFACE) && (el->ifId < MAX_INTERFACE)) { ifList[el->ifId] = 1, foundIf = 1; }
     if((ifId != NO_INTERFACE) && (el->ifId != ifId)) continue;

     if(showBytes) {
       actUsage  = (unsigned short)(0.5+100.0*(((float)el->bytesSent.value+(float)el->bytesRcvd.value)/
					       (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value));
       actUsageS = (unsigned short)(0.5+100.0*((float)el->bytesSent.value/
					       (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value));
       actUsageR = (unsigned short)(0.5+100.0*((float)el->bytesRcvd.value/
					       (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value));
     } else {
       actUsage  = (unsigned short)(0.5+100.0*(((float)el->pktSent.value+(float)el->pktRcvd.value)/
					       (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value));
       actUsageS = (unsigned short)(0.5+100.0*((float)el->pktSent.value/
					       (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value));
       actUsageR = (unsigned short)(0.5+100.0*((float)el->pktRcvd.value/
					       (float)myGlobals.device[myGlobals.actualReportDeviceId].ethernetPkts.value));
     }

     el->actBandwidthUsage = actUsage;
     if(el->actBandwidthUsage > maxBandwidthUsage)
       maxBandwidthUsage = actUsage;
     el->actBandwidthUsageS = actUsageS;
     el->actBandwidthUsageR = actUsageR;

     tmpTable[numEntries++] = el;
     getHostAS(el);

     if(numEntries >= maxHosts)
       break;
   }

   if(numEntries > 0) {
     int i;

     qsort(tmpTable, numEntries, sizeof(HostTraffic*), sortHostFctn);

     safe_snprintf(__FILE__, __LINE__, htmlAnchor, sizeof(htmlAnchor),
		   "<A HREF=\"/%s?col=%s", CONST_HOSTS_INFO_HTML, sign);
     safe_snprintf(__FILE__, __LINE__, htmlAnchor1, sizeof(htmlAnchor1),
		   "<A HREF=\"/%s?col=", CONST_HOSTS_INFO_HTML);

     for(i=1; i< (sizeof(arrow)/sizeof(char*)); i++) {
       if(abs(myGlobals.columnSort) == i)
	 arrow[i] = arrowGif, theAnchor[i] = htmlAnchor;
       else
	 arrow[i] = "", theAnchor[i] = htmlAnchor1;
     }

     if(abs(myGlobals.columnSort) == FLAG_DOMAIN_DUMMY_IDX)
       arrow[0] = arrowGif, theAnchor[0] = htmlAnchor;
     else
       arrow[0] = "", theAnchor[0] = htmlAnchor1;

     sendString("<P ALIGN=LEFT>");

     if(vlanId > 0)
       safe_snprintf(__FILE__, __LINE__, (char*)vlanStr, sizeof(vlanStr), "&vlan=%d", vlanId);
     else
       vlanStr[0] = '\0';

     if(ifId > 0)
       safe_snprintf(__FILE__, __LINE__, (char*)ifStr, sizeof(ifStr), "&if=%d", ifId);
     else
       ifStr[0] = '\0';

     if(showBytes)
       safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		     "<b>Traffic Unit:</b> [ <B>Bytes</B> ]&nbsp;"
		     "[ <A HREF=\"/%s?col=%d&unit=0%s%s\">Packets</A> ]&nbsp;</TD>",
		     CONST_HOSTS_INFO_HTML, myGlobals.columnSort, vlanStr, ifStr);
     else
       safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		     "<b>Traffic Unit:</b> [ <A HREF=\"/%s?col=%d&unit=1%s%s\">Bytes</A> ]&nbsp;"
		     "[ <B>Packets</B> ]&nbsp;</TD>",
		     CONST_HOSTS_INFO_HTML, myGlobals.columnSort, vlanStr, ifStr);

     sendString(buf);
     sendString("</P>\n");

     if(foundVlan) {
       u_char found = 0, tmpBuf[64];

       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>%s</b> ] ",
			   vlan2name(i, (char*)tmpBuf, sizeof(tmpBuf))), found = 1;
	   else
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			   "[ <A HREF=\"/%s?unit=%d&vlan=%d\">%s</A> ] ",
			   CONST_HOSTS_INFO_HTML, showBytes, i,
			   vlan2name(i, (char*)tmpBuf, sizeof(tmpBuf)));

	   sendString(buf);
	 }

     }

     if(foundIf) {
       u_char found = 0;

       sendString("<p><b>Interface Id</b>: ");

       for(i=0; i<MAX_INTERFACE; i++)
	 if(ifList[i] == 1) {
	   if(i == ifId)
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "[ <b>%d</b> ] ", i), found = 1;
	   else
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "[ <A HREF=\"/%s?unit=%d&if=%d\">%d</A> ] ",
			   CONST_HOSTS_INFO_HTML, showBytes, 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?unit=%d\">All</A> ] ",
		       CONST_HOSTS_INFO_HTML, showBytes);

       sendString(buf);
     }

     if(!myGlobals.device[myGlobals.actualReportDeviceId].dummyDevice) {
       safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<CENTER>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR "TR_ON" "DARK_BG">"
		     "<TH "TH_BG">%s1\">Host%s</A></TH>\n"
		     "<TH "TH_BG">%s"FLAG_DOMAIN_DUMMY_IDX_STR"\">Domain%s</A></TH>\n"
		     "<TH "TH_BG">%s2\">IP&nbsp;Address%s</A></TH>\n"
		     "<TH "TH_BG">%s3\">MAC&nbsp;Address%s</A></TH>\n"
		     "<TH "TH_BG">%s11\">Community%s</A></TH>\n"
		     "<TH "TH_BG">%s6\">Other&nbsp;Name(s)%s</A></TH>\n"
		     "<TH "TH_BG">%s4\">Bandwidth%s</A></TH>\n"
		     "<TH "TH_BG">%s5\">Nw&nbsp;Board&nbsp;Vendor%s</A></TH>\n"
		     "<TH "TH_BG">%s7\">Hops&nbsp;Distance%s</A></TH>\n"
		     "<TH "TH_BG">%s8\">Host&nbsp;Contacts%s</A></TH>\n"
		     "<TH "TH_BG" COLSPAN=2>%s9\">Age/Inactivity%s</A></TH>\n"
		     "<TH "TH_BG">%s10\">AS%s</A></TH>\n"

		     "</TR>\n",
		     theAnchor[1], arrow[1],
		     theAnchor[0], arrow[0],
		     theAnchor[2], arrow[2],
		     theAnchor[3], arrow[3],
		     theAnchor[11], arrow[11],
		     theAnchor[6], arrow[6],
		     theAnchor[4], arrow[4],
		     theAnchor[5], arrow[5],
		     theAnchor[7], arrow[7],
		     theAnchor[8], arrow[8],
		     theAnchor[9], arrow[9],
		     theAnchor[10], arrow[10]
		     );
     } else {
       safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<CENTER>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR "TR_ON" "DARK_BG">"
		     "<TH "TH_BG">%s1\">Host%s</A></TH>\n"
		     "<TH "TH_BG">%s"FLAG_DOMAIN_DUMMY_IDX_STR"\">Domain%s</A></TH>\n"
		     "</TH><TH "TH_BG">%s2\">IP&nbsp;Address%s</A></TH>\n"
		     "<TH "TH_BG">%s11\">Community%s</A></TH>"
		     "<TH "TH_BG">%s6\">Other&nbsp;Name(s)%s</A></TH>\n"
		     "<TH "TH_BG">%s4\">Bandwidth%s</A></TH>\n"
		     "<TH "TH_BG">%s7\">Hops&nbsp;Distance%s</A></TH>\n"
		     "<TH "TH_BG">%s8\">Host&nbsp;Contacts%s</A></TH>\n"
		     "<TH "TH_BG" COLSPAN=2>%s9\">Age/Inactivity%s</A></TH>\n"
		     "<TH "TH_BG">%s10\">AS%s</A></TH>\n"
		     "</TR>\n",
		     theAnchor[1], arrow[1],
		     theAnchor[0], arrow[0],
		     theAnchor[2], arrow[2],
		     theAnchor[6], arrow[6],
		     theAnchor[11], arrow[11],
		     theAnchor[4], arrow[4],
		     theAnchor[7], arrow[7],
		     theAnchor[8], arrow[8],
		     theAnchor[9], arrow[9],
		     theAnchor[10], arrow[10]
		     );
     }
     sendString(buf);

     for(idx=pageNum*myGlobals.runningPref.maxNumLines; idx<numEntries; idx++) {
       if(revertOrder)
	 el = tmpTable[numEntries-idx-1];
       else
	 el = tmpTable[idx];

       if(el != NULL) {
	 char *tmpName1, *tmpName2, *tmpName3, sniffedName[MAXDNAME];
	 int displaySniffedName=0;

	 tmpName1 = el->hostNumIpAddress;

	 if((tmpName1[0] == '\0') || (strcmp(tmpName1, "0.0.0.0") == 0))
	   tmpName1 = myGlobals.separator;

	 if(!myGlobals.device[myGlobals.actualReportDeviceId].dummyDevice) {
	   tmpName2 = getVendorInfo(el->ethAddress, 1);
	   if(tmpName2[0] == '\0')
	     tmpName2 = myGlobals.separator;

	   tmpName3 = el->ethAddressString;
	   if((tmpName3[0] == '\0')
	      || (strcmp(tmpName3, "00:00:00:00:00:00") == 0))
	     tmpName3 = myGlobals.separator;
	 } else {
	   tmpName2 = myGlobals.separator;
	   tmpName3 = myGlobals.separator;
	 }

	 if(!addrnull(&el->hostIpAddress)
	    && (getSniffedDNSName(el->hostNumIpAddress,
				  sniffedName, sizeof(sniffedName)))) {
 #ifdef DEBUG
	   traceEvent(CONST_TRACE_INFO, "%s <=> %s [%s/%s]",
		      el->hostNumIpAddress, sniffedName,
		      el->hostResolvedName, el->hostNumIpAddress);
 #endif

	   if((el->hostResolvedName[0] == '\0') || strcmp(sniffedName, el->hostResolvedName)) {
	     if((el->hostResolvedName[0] == '\0')
		|| (strcmp(el->hostResolvedName, el->hostNumIpAddress) == 0)) {
	       if(strlen(sniffedName) >= (MAX_LEN_SYM_HOST_NAME-1))
		 sniffedName[MAX_LEN_SYM_HOST_NAME-2] = '\0';

	       for(i=0; i<strlen(sniffedName); i++) {
		 if(isupper(sniffedName[i])) 
		   sniffedName[i] = tolower(sniffedName[i]);
	       }

	       setResolvedName(el, sniffedName, FLAG_HOST_SYM_ADDR_TYPE_NAME);
	     } else
	       displaySniffedName=1;
	   }
	 }

	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>", getRowColor());
	 sendString(buf);

	 sendString(makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 1, hostLinkBuf, sizeof(hostLinkBuf)));

	 if(!myGlobals.device[myGlobals.actualReportDeviceId].dummyDevice) {
	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			 "<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			 tmpName1, tmpName3);
	 } else {
	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			 tmpName1);
	 }
	 sendString(buf);

	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		       "<TD "TD_BG" ALIGN=RIGHT NOWRAP>%s</TD>",
		       (el->community == NULL) ? "&nbsp;" : el->community);
	 sendString(buf);


	 sendString("<TD "TD_BG" ALIGN=RIGHT NOWRAP>");

	 if(el->nonIPTraffic && displaySniffedName) {
	   short numAddresses = 0;

	   if(el->nonIPTraffic->nbHostName && el->nonIPTraffic->nbDomainName) {
	     if((el->nonIPTraffic->nbAccountName != NULL) && ((el->nonIPTraffic->nbAccountName[0] != '0'))) {
	       if((el->nonIPTraffic->nbDomainName != NULL) && (el->nonIPTraffic->nbDomainName[0] != '0')) {
		 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s&nbsp;%s@%s&nbsp;[%s]", getOSFlag(el, "Windows", 0, osBuf, sizeof(osBuf)),
			       el->nonIPTraffic->nbAccountName, el->nonIPTraffic->nbHostName,
			       el->nonIPTraffic->nbDomainName);
	       } else {
		 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s&nbsp;%s@%s", getOSFlag(el, "Windows", 0, osBuf, sizeof(osBuf)),
			       el->nonIPTraffic->nbAccountName, el->nonIPTraffic->nbHostName);
	       }
	     } else {
	       if((el->nonIPTraffic->nbDomainName != NULL) && (el->nonIPTraffic->nbDomainName[0] != '0')) {
		 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s&nbsp;%s&nbsp;[%s]", getOSFlag(el, "Windows", 0, osBuf, sizeof(osBuf)),
			       el->nonIPTraffic->nbHostName, el->nonIPTraffic->nbDomainName);
	       } else {
		 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s&nbsp;%s", getOSFlag(el, "Windows", 0, osBuf, sizeof(osBuf)),
			       el->nonIPTraffic->nbHostName);
	       }
	     }
	     sendString(buf);
	     numAddresses++;
	   } else if(el->nonIPTraffic->nbHostName) {
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s&nbsp;%s", getOSFlag(el, "Windows", 0, osBuf, sizeof(osBuf)),
			   el->nonIPTraffic->nbHostName);
	     sendString(buf);
	     numAddresses++;
	   }

	   if(el->nonIPTraffic->nbDescr) {
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), ":&nbsp;%s", el->nonIPTraffic->nbDescr);
	     sendString(buf);
	   }

	   if(displaySniffedName) {
	     if(numAddresses > 0) sendString("/");
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s", sniffedName);
	     sendString(buf);
	     numAddresses++;
	   }

	   if(el->nonIPTraffic->atNetwork) {
	     char *nodeName = el->nonIPTraffic->atNodeName;

	     if(numAddresses > 0) sendString("/");
	     if(nodeName == NULL) nodeName = "";

	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s&nbsp;%s&nbsp;",
			   getOSFlag(el, "Mac", 0, osBuf, sizeof(osBuf)), nodeName);
	     sendString(buf);

	     if(el->nonIPTraffic->atNodeType[0] != NULL) {
	       sendString("(");
	       for(i=0; i<MAX_NODE_TYPES; i++)
		 if(el->nonIPTraffic->atNodeType[i] == NULL)
		   break;
		 else {
		   if(i > 0) sendString("/");
		   sendString(el->nonIPTraffic->atNodeType[i]);
		 }

	       sendString(")&nbsp;");
	     }

	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "[%d.%d]",
			   el->nonIPTraffic->atNetwork, el->nonIPTraffic->atNode);
	     sendString(buf);
	     numAddresses++;
	   }

	   if(el->nonIPTraffic->ipxHostName) {
	     int numSap=0;

	     if(numAddresses > 0) sendString("/");
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s&nbsp;%s&nbsp;",
			   getOSFlag(el, "Novell", 0, osBuf, sizeof(osBuf)),
			   el->nonIPTraffic->ipxHostName);
	     sendString(buf);

	     for(i=0; i<el->nonIPTraffic->numIpxNodeTypes; i++) {
	       char *str = getSAPInfo(el->nonIPTraffic->ipxNodeType[i], 1);

	       if(str[0] != '\0') {
		 if(numSap == 0)
		   sendString("[");
		 else
		   sendString("/");

		 sendString(str);
		 numSap++;
	       }
	     }

	     if(numSap > 0) sendString("]");

	     numAddresses++;
	   }
	 }

	 sendString("&nbsp;</TD>");
	 printBar(buf, sizeof(buf), el->actBandwidthUsageS, el->actBandwidthUsageR, maxBandwidthUsage, 3);

	 if(!myGlobals.device[myGlobals.actualReportDeviceId].dummyDevice) {
	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT NOWRAP>%s</TD>", tmpName2);
	   sendString(buf);
	 }

	 {
	   char shortBuf[8];

	   if(!subnetPseudoLocalHost(el)) {
	     i = guessHops(el);
	   } else
	     i = 0;

	   safe_snprintf(__FILE__, __LINE__, shortBuf, sizeof(shortBuf), "%d", i % 256);

	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>&nbsp;%s</TD>",
			 (i == 0) ? "" : shortBuf);
	   sendString(buf);
	 }

	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%lu</TD>",
		       (unsigned long)(el->totContactedSentPeers+el->totContactedRcvdPeers));
	 sendString(buf);

 #if 0
	 /* Time distance */
	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s-",
		       formatLatency(el->minLatency, FLAG_STATE_ACTIVE));
	 sendString(buf);

	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s</TD>",
		       formatLatency(el->maxLatency, FLAG_STATE_ACTIVE));
	 sendString(buf);
 #endif

	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<td "TD_BG" align=\"right\" nowrap>%s</td>",
		       formatSeconds(el->lastSeen - el->firstSeen, formatBuf, sizeof(formatBuf)));
	 sendString(buf);

	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<td "TD_BG" align=\"right\" nowrap>%s</td>",
		       formatSeconds(myGlobals.actTime-el->lastSeen, formatBuf, sizeof(formatBuf)));
	 sendString(buf);

	 if(el->hostAS == 0) {
	   sendString("<TD "TD_BG" ALIGN=RIGHT NOWRAP>&nbsp;</TD>");
	 } else {
	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			 "<TD "TD_BG" ALIGN=RIGHT NOWRAP>"
			 "<a href=\"" DEFAULT_AS_LOOKUP_URL "%d\" title=\"Lookup ASN (offsite)\">%d</a>"
			 "</TD>",
			 el->hostAS, el->hostAS);
	   sendString(buf);
	 }

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

	 printedEntries++;

	 /* Avoid huge tables */
	 if(printedEntries > myGlobals.runningPref.maxNumLines)
	   break;
       } else {
	 traceEvent(CONST_TRACE_WARNING, "qsort() problem!");
       }
     }

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

     printFooterHostLink();

     printBandwidthFooter();

     addPageIndicator(CONST_HOSTS_INFO_HTML, pageNum, numEntries, myGlobals.runningPref.maxNumLines,
		      revertOrder, abs(sortedColumn), -1);
   }

   free(tmpTable);
   free(vlanList);
   free(ifList);
 }

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

 void printAllSessionsHTML(char* host, int actualDeviceId, int sortedColumn,
			   int revertOrder, int pageNum, char *url,
			   int hostInfoPage) {
   u_int idx, i;
   u_int16_t vlanId = NO_VLAN;
   HostTraffic *el=NULL;
   char buf[LEN_GENERAL_WORK_BUFFER];
   char formatBuf[32], portBuf[32], hostLinkBuf[LEN_GENERAL_WORK_BUFFER];
   u_short found = 0,
     foundFcHost = 0,
     vsanId = 0;
   char *tok;

   if((tok = strchr(host, '-')) != NULL) {
     vlanId = vsanId = atoi(&tok[1]);
     *tok = '\0';
   }

   for(el=getFirstHost(actualDeviceId);
       el != NULL; el = getNextHost(actualDeviceId, el)) {
     if(el->community && (!isAllowedCommunity(el->community))) continue;

     if(((strcmp(el->hostNumIpAddress, host) == 0) || (strcmp(el->ethAddressString, host) == 0))
	&& ((vlanId == NO_VLAN) || ((el->vlanId <= 0) || (el->vlanId == vlanId)))) {
       found = 1;
       break;
     } else if((el->fcCounters != NULL)
	       && ((strncmp(fc_to_str ((u_int8_t *)&el->fcCounters->hostFcAddress),
			    host, LEN_FC_ADDRESS_DISPLAY) == 0) &&
		   ((el->fcCounters->vsanId == vsanId) || (vsanId == 0)))) {
       found = 1;
       foundFcHost = 1;
       break;
     }
   }

   /* Dennis Schoen (dennis@cns.dnsalias.org)
    *
    * send 404 if we cannot generate the requested page
    */
   if((el == NULL) || (!found)) {
     char errorAdditionalText[1024], whois[256];

     safe_snprintf(__FILE__, __LINE__, whois, sizeof(whois),
		   "[ <A class=external HREF=\"http://www.radb.net/cgi-bin/radb/whois.cgi?obj=%s\">Whois</A> ]</TD></TR>\n",
		   host);

     safe_snprintf(__FILE__, __LINE__, errorAdditionalText, sizeof(errorAdditionalText),
		   "<p align=\"center\"><img class=tooltip alt=\"Warning\" src=\"/warning.gif\"></p>\n"
		   "<p align=\"center\"><font color=\"#FF0000\" size=\"+1\">"
		   "<b>ntop</b> does not currently have any information about host %s %s.</font></p>"
		   "<p>&nbsp;</p>"
		   "<p>This is most likely because the host information has been "
		   "purged as inactive.  You may wish to consider the -c | --sticky-hosts "
		   "option, although that option may substantially increase memory "
		   "requirements.</p>\n",
		   host, whois);
     returnHTTPpageNotFound(errorAdditionalText);
     return;
   }

   if(el->community && (!isAllowedCommunity(el->community))) {
     returnHTTPpageBadCommunity();
     return;
   }

   sendHTTPHeader(FLAG_HTTP_TYPE_HTML, 0, 1);

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

   if(found && !foundFcHost) {
     printHostDetailedInfo(el, actualDeviceId);
     printHostTrafficStats(el, actualDeviceId);
     printHostIcmpStats(el);
     printHostFragmentStats(el, actualDeviceId);
     printHostContactedPeers(el, actualDeviceId);
     printHostHTTPVirtualHosts(el, actualDeviceId);
     printHostUsedServices(el, actualDeviceId);
   } else if(foundFcHost) {
     printHTMLheader("", NULL, 0);
     printFcHostHeader(el, url, revertOrder, sortedColumn, hostInfoPage);

     switch (hostInfoPage) {
     case showHostMainPage:
       printFcHostDetailedInfo (el, actualDeviceId);
       printFcHostTrafficStats (el, actualDeviceId);
       printFcHostContactedPeers(el, actualDeviceId);
       break;
     case showHostLunStats:
       if(el->fcCounters->devType != SCSI_DEV_INITIATOR) {
	 printScsiLunStats (el, actualDeviceId, sortedColumn,
			    revertOrder, pageNum, url);
       }
       break;
     case showHostLunGraphs:
       if(el->fcCounters->devType != SCSI_DEV_INITIATOR) {
	 printScsiLunGraphs (el, actualDeviceId);
       }
       break;
     case showHostScsiSessionBytes:
       printScsiSessionBytes (actualDeviceId, sortedColumn, revertOrder,
			      pageNum, url, el);
       break;
     case showHostScsiSessionTimes:
       printScsiSessionTimes (actualDeviceId, sortedColumn, revertOrder,
			      pageNum, url, el);
       break;
     case showHostScsiSessionStatus:
       printScsiSessionStatusInfo (actualDeviceId, sortedColumn,
				   revertOrder, pageNum, url, el);
       break;
     case showHostScsiSessionTMInfo:
       printScsiSessionTmInfo (actualDeviceId, sortedColumn,
			       revertOrder, pageNum, url, el);
       break;
     case showHostFcSessions:
       printFCSessions (actualDeviceId, sortedColumn,
			revertOrder, pageNum, url, el);
       break;
     }


     return;
   }

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

   i = 0;

   if(el->portsUsage != NULL) {
     for(idx=1; idx<MAX_ASSIGNED_IP_PORTS /* 1024 */; idx++) {
       PortUsage *ports = getPortsUsage(el, idx, 0);
       if(ports != NULL) {
	 char *svc = getAllPortByNum(idx, portBuf, sizeof(portBuf));
	 char webHostName[LEN_GENERAL_WORK_BUFFER];
	 HostTraffic *peerHost;

	 if(i == 0) {
	   printSectionTitle("TCP/UDP&nbsp;Service/Port&nbsp;Usage\n");
	   sendString("<CENTER>\n");
	   sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR "TR_ON" "DARK_BG">"
		      "<TH "TH_BG">IP&nbsp;Service</TH>"
		      "<TH "TH_BG">Port</TH>"
		      "<TH "TH_BG">#&nbsp;Client&nbsp;Sess.</TH>"
		      "<TH "TH_BG">Last&nbsp;Client&nbsp;Peer</TH>"
		      "<TH "TH_BG">#&nbsp;Server&nbsp;Sess.</TH>"
		      "<TH "TH_BG">Last&nbsp;Server&nbsp;Peer</TH>"
		      "</TR>\n");
	   i++;
	 }

	 if(svc != 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>%d</TD>", getRowColor(), svc, idx);
	 } else {
	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=LEFT "DARK_BG">%d</TH>"
			 "<TD "TD_BG" ALIGN=CENTER>%d</TD>", getRowColor(), idx, idx);
	 }

	 sendString(buf);

	 if(ports->clientUses > 0) {
	   /* Fix below courtesy of Andreas Pfaller <apfaller@yahoo.com.au> */
	   HostTraffic tmpEl;

	   if(emptySerial(&ports->clientUsesLastPeer))
	     peerHost = NULL;
	   else
	     peerHost = quickHostLink(ports->clientUsesLastPeer, actualDeviceId, &tmpEl);

	   if(peerHost == NULL) {
	     /* Courtesy of Roberto De Luca <deluca@tandar.cnea.gov.ar> */
	     strncpy(webHostName, "&nbsp;", sizeof(webHostName));
	   } else
	     strncpy(webHostName, makeHostLink(peerHost, FLAG_HOSTLINK_TEXT_FORMAT, 0,
					       0, hostLinkBuf, sizeof(hostLinkBuf)),
		     sizeof(webHostName));

	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%d/%s</TD>"
			 "<TD "TD_BG" ALIGN=CENTER>%s</TD>",
			 ports->clientUses,
			 formatBytes(ports->clientTraffic.value, 1, formatBuf, sizeof(formatBuf)),
			 webHostName);
	   sendString(buf);
	 } else
	   sendString("<TD "TD_BG">&nbsp;</TD><TD "TD_BG">&nbsp;</TD>");

	 if(ports->serverUses > 0) {
	   HostTraffic tmpEl;

	   if(emptySerial(&ports->serverUsesLastPeer))
	     peerHost = NULL;
	   else
	     peerHost = quickHostLink(ports->serverUsesLastPeer, actualDeviceId, &tmpEl);

	   if(peerHost == NULL) {
	     /* Courtesy of Roberto De Luca <deluca@tandar.cnea.gov.ar> */
	     strncpy(webHostName, "&nbsp;", sizeof(webHostName));
	   } else
	     strncpy(webHostName, makeHostLink(peerHost, FLAG_HOSTLINK_TEXT_FORMAT, 0,
					       0, hostLinkBuf, sizeof(hostLinkBuf)), sizeof(webHostName));

	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER>%d/%s</TD>"
			 "<TD "TD_BG" ALIGN=CENTER>%s</TD></TR>",
			 ports->serverUses,
			 formatBytes(ports->serverTraffic.value, 1, formatBuf, sizeof(formatBuf)),
			 webHostName);
	   sendString(buf);
	 } else
	   sendString("<TD "TD_BG">&nbsp;</TD><TD "TD_BG">&nbsp;</TD></TR>");
       }
     }
   }

   if(i > 0){
     sendString("</TABLE>"TABLE_OFF"<P>\n");
     sendString("</CENTER>\n");
   }

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

   if((el->otherIpPortsRcvd[MAX_NUM_RECENT_PORTS-1] >= 0) || (el->otherIpPortsSent[MAX_NUM_RECENT_PORTS-1] >= 0)) {
     /* We have something to show */
     int numPrinted;

     printSectionTitle("TCP/UDP - Traffic on Other Ports\n");
     sendString("<CENTER>\n");
     sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR "TR_ON" "DARK_BG">"
		"<TH "TH_BG">Client Port</TH><TH "TH_BG">Server Port</TH>"
		"</TR>\n");

     sendString("<TR "TR_ON"><TD "TD_BG" ALIGN=LEFT><UL>");

     for(idx=0, numPrinted=0; idx<MAX_NUM_RECENT_PORTS; idx++) {
       if(el->otherIpPortsSent[idx] >= 0) {
	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		       "<LI><A HREF=\"" CONST_SHOW_PORT_TRAFFIC_HTML "?port=%d\">%s</A>\n",
		       el->otherIpPortsSent[idx],
		       getAllPortByNum(el->otherIpPortsSent[idx], portBuf, sizeof(portBuf)));
	 sendString(buf);
	 numPrinted++;
       }
     }

     if(numPrinted == 0) sendString("&nbsp;");
     sendString("</UL></TD><TD "TD_BG" ALIGN=LEFT><UL>");

     for(idx=0, numPrinted=0; idx<MAX_NUM_RECENT_PORTS; idx++) {
       if(el->otherIpPortsRcvd[idx] >= 0) {
	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		       "<li><A HREF=\"" CONST_SHOW_PORT_TRAFFIC_HTML "?port=%d\">%s</A>\n",
		       el->otherIpPortsRcvd[idx],
		       getAllPortByNum(el->otherIpPortsRcvd[idx], portBuf, sizeof(portBuf)));
	 sendString(buf);
	 numPrinted++;
       }
     }

     if(numPrinted == 0) sendString("&nbsp;");
     sendString("</UL></TR></TABLE>"TABLE_OFF"</CENTER>");
   }

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

   if((el->recentlyUsedClientPorts[MAX_NUM_RECENT_PORTS-1] >= 0)
      || (el->recentlyUsedServerPorts[MAX_NUM_RECENT_PORTS-1] >= 0)) {
     /* We have something to show */
     int numPrinted;

     printSectionTitle("TCP/UDP Recently Used Ports\n");
     sendString("<CENTER>\n");
     sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR "TR_ON" "DARK_BG">"
		"<TH "TH_BG">Client Port</TH><TH "TH_BG">Server Port</TH>"
		"</TR>\n");

     sendString("<TR "TR_ON"><TD "TD_BG" ALIGN=LEFT><UL>");

     for(idx=0, numPrinted=0; idx<MAX_NUM_RECENT_PORTS; idx++) {
       if(el->recentlyUsedClientPorts[idx] >= 0) {
	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		       "<li><A HREF=\"" CONST_SHOW_PORT_TRAFFIC_HTML "?port=%d\">%s</A>\n",
		       el->recentlyUsedClientPorts[idx],
		       getAllPortByNum(el->recentlyUsedClientPorts[idx], portBuf, sizeof(portBuf)));
	 sendString(buf);
	 numPrinted++;
       }
     }

     if(numPrinted == 0) sendString("&nbsp;");

     sendString("</UL></TD><TD "TD_BG" ALIGN=LEFT><UL>");

     for(idx=0, numPrinted=0; idx<MAX_NUM_RECENT_PORTS; idx++) {
       if(el->recentlyUsedServerPorts[idx] >= 0) {
	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		       "<LI><A HREF=\"" CONST_SHOW_PORT_TRAFFIC_HTML "?port=%d\">%s</A>\n",
		       el->recentlyUsedServerPorts[idx],
		       getAllPortByNum(el->recentlyUsedServerPorts[idx], portBuf, sizeof(portBuf)));
	 sendString(buf);
	 numPrinted++;
       }
     }

     if(numPrinted == 0) sendString("&nbsp;");
     sendString("</UL></TR></TABLE>"TABLE_OFF"</CENTER>");
   }

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

   if((el->protocolInfo != NULL)
      && (el->protocolInfo->fileList != NULL)) {
     FileList *list = el->protocolInfo->fileList;

     printSectionTitle("P2P Recently Exchanged Files\n");

     sendString("<CENTER>\n");
     sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR "TR_ON">"
		"<TH "TH_BG" NOWRAP>File Name</TH></TR>\n");
     sendString("<TR><TD align=left NOWRAP><ol>\n");

     while(list != NULL) {
       safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<li>%s&nbsp;",
		     list->fileName);
       sendString(buf);

       if(FD_ISSET(BITFLAG_P2P_UPLOAD_MODE, &list->fileFlags))
	 sendString("<IMG SRC=/upload.gif ALT=Upload VALIGN=MIDDLE>&nbsp;");
       if(FD_ISSET(BITFLAG_P2P_DOWNLOAD_MODE, &list->fileFlags))
	 sendString("<IMG SRC=/download.gif ALT=Download VALIGN=MIDDLE>&nbsp;");

       list = list->next;
     }

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

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

   printHostSessions(el, actualDeviceId);
 }

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

 void printLocalRoutersList(int actualDeviceId) {
   char buf[LEN_GENERAL_WORK_BUFFER], hostLinkBuf[LEN_GENERAL_WORK_BUFFER];
   HostTraffic *el, *router;
   u_int i, j, numEntries=0;
   HostSerial routerList[MAX_NUM_ROUTERS];

   printHTMLheader("Local Subnet Routers", NULL, 0);

   if(myGlobals.runningPref.dontTrustMACaddr) {
     printNotAvailable("-o or --no-mac");
     return;
   }

   for(el=getFirstHost(actualDeviceId);
       el != NULL; el = getNextHost(actualDeviceId, el)) {

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

     if(subnetLocalHost(el)) {
       for(j=0; j<MAX_NUM_CONTACTED_PEERS; j++)
	 if(!emptySerial(&el->contactedRouters.peersSerials[j])) {
	   short found = 0;

	   for(i=0; i<numEntries; i++) {
	     if(cmpSerial(&el->contactedRouters.peersSerials[j], &routerList[i])) {
	       found = 1;
	       break;
	     }
	   }

	   if((found == 0) && (numEntries < MAX_NUM_ROUTERS)) {
	     routerList[numEntries++] = el->contactedRouters.peersSerials[j];
	   }
	 }
     }
   } /* for */

   if(numEntries == 0) {
     printNoDataYet();
     return;
   } else {
     sendString("<CENTER>\n");
     sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "TR_ON" "DARK_BG"><TH "TH_BG">Router Name</TH>"
		"<TH "TH_BG">Used by</TH></TR>\n");

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

       if((router = quickHostLink(routerList[i], myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		       "<TR "TR_ON" %s><TH "TH_BG" align=left>%s</TH><TD "TD_BG" ALIGN=LEFT><UL>\n",
		       getRowColor(),
		       makeHostLink(router, FLAG_HOSTLINK_TEXT_FORMAT, 0, 0,
				    hostLinkBuf, sizeof(hostLinkBuf)));
	 sendString(buf);


	 for(el=getFirstHost(actualDeviceId); el != NULL; el = getNextHost(actualDeviceId, el)) {
	   if(el->community && (!isAllowedCommunity(el->community))) continue;
	   if(subnetLocalHost(el)) {
	     for(j=0; j<MAX_NUM_CONTACTED_PEERS; j++)
	       if(cmpSerial(&el->contactedRouters.peersSerials[j], &routerList[i])) {
		 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<LI>%s</LI>\n",
			       makeHostLink(el, FLAG_HOSTLINK_TEXT_FORMAT, 0, 0,
					    hostLinkBuf, sizeof(hostLinkBuf)));
		 sendString(buf);
		 break;
	       }
	   }
	 }

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

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

     printHostColorCode(FALSE, 0);

     printFooterHostLink();
   }
 }

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

 void printIpAccounting(int remoteToLocal, int sortedColumn,
			int revertOrder, int pageNum) {
   u_int idx, numEntries=0, maxHosts;
   int printedEntries=0;
   HostTraffic *el, **tmpTable;
   char buf[LEN_GENERAL_WORK_BUFFER], *str=NULL, *sign, *title=NULL;
   Counter totalBytesSent, totalBytesRcvd, totalBytes, a=0, b=0;
   float sentpct, rcvdpct;
   time_t timeDiff = time(NULL)-myGlobals.initialSniffTime;
   char *arrowGif, *arrow[48], *theAnchor[48], hostLinkBuf[LEN_GENERAL_WORK_BUFFER];
   char htmlAnchor[64], htmlAnchor1[64];
   char formatBuf[32], formatBuf1[32], formatBuf2[32], formatBuf3[32];

   switch(remoteToLocal) {
   case FLAG_REMOTE_TO_LOCAL_ACCOUNTING:
     str = CONST_IP_R_2_L_HTML;
     title = "Remote to Local IP Traffic";
     break;
   case FLAG_REMOTE_TO_REMOTE_ACCOUNTING:
     str = CONST_IP_R_2_R_HTML;
     title = "Remote to Remote IP Traffic";
     break;
   case FLAG_LOCAL_TO_REMOTE_ACCOUNTING:
     str = CONST_IP_L_2_R_HTML;
     title = "Local to Remote IP Traffic";
     break;
   case FLAG_LOCAL_TO_LOCAL_ACCOUNTING:
     str = CONST_IP_L_2_L_HTML;
     title = "Local IP Traffic";
     break;
   }

   printHTMLheader(title, NULL, 0);

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

   totalBytesSent=0, totalBytesRcvd=0;
   maxHosts = myGlobals.device[myGlobals.actualReportDeviceId].hostsno; /* save it as it can change */

   tmpTable = (HostTraffic**)mallocAndInitWithReportWarn(maxHosts*sizeof(HostTraffic*), "printIpAccounting");
   if(tmpTable == NULL)
     return;

   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 */
	&& ((el->hostNumIpAddress[0] != '\0')
	    && (!addrnull(&el->hostIpAddress))
	    /* This host speaks IP */)) {

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

       switch(remoteToLocal) {
       case FLAG_REMOTE_TO_LOCAL_ACCOUNTING:
	 if(!subnetPseudoLocalHost(el)) {
	   if((el->bytesSentLoc.value > 0) || (el->bytesRcvdLoc.value > 0)) {
	     tmpTable[numEntries++]=el;
	     totalBytesSent += el->bytesSentLoc.value;
	     totalBytesRcvd += el->bytesRcvdLoc.value;
	   }
	 }
	 break;
       case FLAG_REMOTE_TO_REMOTE_ACCOUNTING:
	 if(!subnetPseudoLocalHost(el)) {
	   if((el->bytesSentRem.value > 0) || (el->bytesRcvdFromRem.value > 0)) {
	     tmpTable[numEntries++]=el;
	     totalBytesSent += el->bytesSentRem.value;
	     totalBytesRcvd += el->bytesRcvdFromRem.value;
	   }
	 }
	 break;
       case FLAG_LOCAL_TO_REMOTE_ACCOUNTING:
	 if(subnetPseudoLocalHost(el)) {
	   if((el->bytesSentRem.value > 0) || (el->bytesRcvdFromRem.value > 0)) {
	     tmpTable[numEntries++]=el;
	     totalBytesSent += el->bytesSentRem.value;
	     totalBytesRcvd += el->bytesRcvdFromRem.value;
	   }
	 }
	 break;
       case FLAG_LOCAL_TO_LOCAL_ACCOUNTING:
	 if(subnetPseudoLocalHost(el)) {
	   if((el->bytesSentLoc.value > 0) || (el->bytesRcvdLoc.value > 0)) {
	     tmpTable[numEntries++]=el;
	     totalBytesSent += el->bytesSentLoc.value;
	     totalBytesRcvd += el->bytesRcvdLoc.value;
	   }
	 }
	 break;
       }

       if(numEntries >= maxHosts) break;
     }
   }

   if(numEntries > 0) {
     int i;

     myGlobals.columnSort = sortedColumn;
     myGlobals.sortFilter = remoteToLocal;
     qsort(tmpTable, numEntries, sizeof(HostTraffic*), cmpHostsFctn);

     safe_snprintf(__FILE__, __LINE__, htmlAnchor, sizeof(htmlAnchor), "<A HREF=/%s?col=%s", str, sign);
     safe_snprintf(__FILE__, __LINE__, htmlAnchor1, sizeof(htmlAnchor1), "<A HREF=/%s?col=", str);

     for(i=1; i<=4; i++)
       if(abs(myGlobals.columnSort) == i)
	 arrow[i] = arrowGif, theAnchor[i] = htmlAnchor;
       else
	 arrow[i] = "", theAnchor[i] = htmlAnchor1;

     sendString("<CENTER>\n");
     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), ""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"80%%\">\n"
		   "<TR "TR_ON" "DARK_BG"><TH "TH_BG">"
		   "%s1>Host%s</A></TH>"
		   "<TH "TH_BG">%s2>IP&nbsp;Address%s</A></TH>\n"
		   "<TH "TH_BG" COLSPAN=2>%s3>Data&nbsp;Sent%s</A></TH>"
		   "<TH "TH_BG" COLSPAN=2>%s4>Data&nbsp;Rcvd%s</A></TH></TR>\n",
		   theAnchor[1], arrow[1],
		   theAnchor[2], arrow[2], theAnchor[3], arrow[3],
		   theAnchor[4], arrow[4]);

     sendString(buf);

     for(idx=pageNum*myGlobals.runningPref.maxNumLines; idx<numEntries; idx++) {
       if(revertOrder)
	 el = tmpTable[numEntries-idx-1];
       else
	 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;

	 switch(remoteToLocal) {
	 case FLAG_REMOTE_TO_LOCAL_ACCOUNTING:
	   a = el->bytesSentLoc.value;
	   b = el->bytesRcvdLoc.value;
	   break;
	 case FLAG_REMOTE_TO_REMOTE_ACCOUNTING:
	   a = el->bytesSentRem.value;
	   b = el->bytesRcvdFromRem.value;
	   break;
	 case FLAG_LOCAL_TO_REMOTE_ACCOUNTING:
	   a = el->bytesSentRem.value;
	   b = el->bytesRcvdFromRem.value;
	   break;
	 case FLAG_LOCAL_TO_LOCAL_ACCOUNTING:
	   a = el->bytesSentLoc.value;
	   b = el->bytesRcvdLoc.value;
	   break;
	 }

	 if(a < 100)  /* Avoid very small decimal values */
	   sentpct = 0;
	 else
	   sentpct = (100*(float)a)/totalBytesSent;

	 if(b < 100)  /* Avoid very small decimal values */
	   rcvdpct = 0;
	 else
	   rcvdpct = (100*(float)b)/totalBytesRcvd;

	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>"
		       "%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		       "</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%s%%</TD>"
		       "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%s%%</TD></TR>\n",
		       getRowColor(),
		       makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
		       tmpName1,
		       formatBytes(a, 1, formatBuf, sizeof(formatBuf)),
		       sentpct, myGlobals.separator,
		       formatBytes(b, 1, formatBuf1, sizeof(formatBuf1)),
		       rcvdpct, myGlobals.separator);
	 sendString(buf);

	 /* Avoid huge tables */
	 if(printedEntries++ > myGlobals.runningPref.maxNumLines)
	   break;
       }
     }

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

     addPageIndicator(str, pageNum, numEntries, myGlobals.runningPref.maxNumLines,
		      revertOrder, abs(sortedColumn), -1);

     sendString("<P>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"80%\">\n<TR "TR_ON" "DARK_BG">"
		"<TH "TH_BG">Total Traffic</TH><TH "TH_BG">Data Sent</TH>\n"
		"<TH "TH_BG">Data Rcvd</TH><TH "TH_BG">Used Bandwidth</TH></TR>\n");

     totalBytes = totalBytesSent+totalBytesRcvd;

     /* In this case the total traffic is just half and
	the following statement holds:
	totalBytesSent == totalBytesRcvd

	Courtesy of Jac Engel <jacengel@home.nl>
     */
     if(remoteToLocal == FLAG_LOCAL_TO_LOCAL_ACCOUNTING)
       totalBytes /= 2;

     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">"
		   "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		   "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		   "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		   "<TD "TD_BG" ALIGN=RIGHT>%s</TD></TR>\n",
		   formatBytes(totalBytes, 1, formatBuf, sizeof(formatBuf)),
		   formatBytes(totalBytesSent, 1, formatBuf1, sizeof(formatBuf1)),
		   formatBytes(totalBytesRcvd, 1, formatBuf2, sizeof(formatBuf2)),
		   formatThroughput((float)(totalBytes/timeDiff), 1, formatBuf3, sizeof(formatBuf3)));

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

     printFooterHostLink();

   } else
     printNoDataYet();

   free(tmpTable);
 }

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

 static char* print_flags(IPSession *session, char *buf, int buf_len) {
   snprintf(buf, buf_len,"%s%s%s%s%s&nbsp;",
	    (session->lastFlags & TH_SYN) ? " SYN" : "",
	    (session->lastFlags & TH_ACK) ? " ACK" : "",
	    (session->lastFlags & TH_FIN) ? " FIN" : "",
	    (session->lastFlags & TH_RST) ? " RST" : "",
	    (session->lastFlags & TH_PUSH) ? " PUSH" : "");  

   return(buf);
 }

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

 void printActiveTCPSessions(int actualDeviceId, int pageNum, HostTraffic *el) {
   int idx;
   char buf[1500], hostLinkBuf[LEN_GENERAL_WORK_BUFFER], flags_buf[64],
     hostLinkBuf1[LEN_GENERAL_WORK_BUFFER], *voipStr;
   int numSessions, printedSessions;
   char formatBuf[64], formatBuf1[64], formatBuf2[64], formatBuf3[64],
     formatBuf4[64], formatBuf5[64], formatBuf6[64];

   if(!myGlobals.runningPref.enableSessionHandling) {
     if(el != NULL) return;
     printHTMLheader("Active TCP/UDP Sessions", NULL, 0);
     printNotAvailable("-z or --disable-sessions");
     return;
   }

   if((myGlobals.device[actualDeviceId].tcpSession == NULL) ||
      (myGlobals.device[actualDeviceId].numTcpSessions == 0)) {
     if(el != NULL) return;
     printHTMLheader("Active TCP/UDP Sessions", NULL, 0);
     printNoDataYet();
     return;
   }

   /*
     Due to the way sessions are handled, sessions before those to
     display need to be skipped
   */
   for(idx=1, numSessions=0, printedSessions=0; idx<MAX_TOT_NUM_SESSIONS; idx++) {
     if(el && (printedSessions >= el->numHostSessions)) break;
     accessMutex(&myGlobals.tcpSessionsMutex, "printActiveTCPSessions");

     if(myGlobals.device[myGlobals.actualReportDeviceId].tcpSession[idx] != NULL) {
       char *sport, *dport;
       Counter dataSent, dataRcvd;
       IPSession *session = myGlobals.device[myGlobals.actualReportDeviceId].tcpSession[idx];

       while((session != NULL) && (printedSessions < myGlobals.runningPref.maxNumLines)) {
 #ifndef PARM_PRINT_ALL_SESSIONS
	 if(session->sessionState != FLAG_STATE_ACTIVE) {
	   session = session->next;
	   continue;
	 }
 #endif

	 if(el && (session->initiator  != el) && (session->remotePeer != el)) {
	   session = session->next;
	   continue;
	 }

	 if((numSessions++) < pageNum*myGlobals.runningPref.maxNumLines) {
	   session = session->next;
	   continue;
	 }

	 if(printedSessions == 0) {
	   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%d Active TCP/UDP Sessions",
			 myGlobals.device[actualDeviceId].numTcpSessions);			

	   if(el == NULL)
	     printHTMLheader(buf, NULL, 0);
	   else
	     printSectionTitle(buf);

	   sendString("<CENTER>\n"
		      ""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "TR_ON" "DARK_BG">"
		      "<TH "TH_BG">Client</TH>"
		      "<TH "TH_BG">Server</TH>"
		      "<TH "TH_BG">Data&nbsp;Sent</TH>"
		      "<TH "TH_BG">Data&nbsp;Rcvd</TH>"
		      "<TH "TH_BG">Active&nbsp;Since</TH>"
		      "<TH "TH_BG">Last&nbsp;Seen</TH>"
		      "<TH "TH_BG">Duration</TH>"
		      "<TH "TH_BG">Inactive</TH>"
		      "<TH "TH_BG">Latency</TH>"
		      "<TH "TH_BG">L7 Proto</TH>"
		      "<TH "TH_BG">Note</TH>");
 #ifdef PARM_PRINT_ALL_SESSIONS
	   sendString("<TH "TH_BG">State</TH>");
 #endif
	   sendString("</TR>\n");
	 }

	 sport = getPortByNum(session->sport, IPPROTO_TCP);
	 dport = getPortByNum(session->dport, IPPROTO_TCP);
	 dataSent = session->bytesSent.value;
	 dataRcvd = session->bytesRcvd.value;

	 if(sport == NULL) {
	   static char _sport[8];
	   safe_snprintf(__FILE__, __LINE__, _sport, 8, "%d", session->sport);
	   sport = _sport;
	 }

	 if(dport == NULL) {
	   static char _dport[8];
	   safe_snprintf(__FILE__, __LINE__, _dport, 8, "%d", session->dport);
	   dport = _dport;
	 }

	 /* Sanity check */
	 if((myGlobals.actTime < session->firstSeen)
	    || (session->firstSeen == 0))
	   session->firstSeen = myGlobals.actTime;
	 if((myGlobals.actTime < session->lastSeen)
	    || (session->lastSeen == 0))
	   session->lastSeen = myGlobals.actTime;

	 if((session->guessed_protocol != NULL)
	    && session->voipSession
	    && strstr(session->guessed_protocol, "skype"))
	   voipStr = "/skype.gif";
	 else if(session->voipSession)
	   voipStr = "&nbsp&lt;VoIP&gt;";
	 else
	   voipStr = "";

	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>"
		       "<TD "TD_BG" ALIGN=RIGHT NOWRAP>%s:%s%s%s</TD>"
		       "<TD "TD_BG" ALIGN=RIGHT NOWRAP>%s:%s</TD>",
		       getRowColor(),
		       makeHostLink(session->initiator, FLAG_HOSTLINK_TEXT_FORMAT,
				    0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
		       sport, session->isP2P == 1 ? "&nbsp&lt;P2P&gt;" : "",
		       voipStr, makeHostLink(session->remotePeer,
					     FLAG_HOSTLINK_TEXT_FORMAT,
					     0, 0, hostLinkBuf1,
					     sizeof(hostLinkBuf1)),
		       dport);
	 sendString(buf);

	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		       "<TD "TD_BG" ALIGN=RIGHT NOWRAP>%s</TD>"
		       "<TD "TD_BG" ALIGN=RIGHT NOWRAP>%s</TD>"
		       "<TD "TD_BG" ALIGN=RIGHT NOWRAP>%s</TD>"
		       "<TD "TD_BG" ALIGN=RIGHT NOWRAP>%s</TD>"
		       "<TD "TD_BG" ALIGN=RIGHT NOWRAP>%s</TD>"
		       "<TD "TD_BG" ALIGN=RIGHT NOWRAP>%s</TD>"
		       "<TD "TD_BG" ALIGN=RIGHT NOWRAP>%s</TD>"
		       "<TD "TD_BG" ALIGN=CENTER NOWRAP>%s</TD>"
		       "<TD "TD_BG" ALIGN=LEFT NOWRAP>%s</TD>",
		       formatBytes(dataSent, 1, formatBuf, sizeof(formatBuf)),
		       formatBytes(dataRcvd, 1, formatBuf1, sizeof(formatBuf1)),
		       formatTime(&(session->firstSeen), formatBuf2, sizeof(formatBuf2)),
		       formatTime(&(session->lastSeen), formatBuf3, sizeof(formatBuf3)),
		       formatSeconds(session->lastSeen-session->firstSeen, formatBuf4, sizeof(formatBuf4)),
		       formatSeconds(myGlobals.actTime-session->lastSeen, formatBuf5, sizeof(formatBuf5)),
		       formatLatency(session->nwLatency, session->sessionState, formatBuf6, sizeof(formatBuf6)),
		       (session->guessed_protocol == NULL) ? "&nbsp;" : session->guessed_protocol,
		       session->session_info ? session->session_info : 
		       print_flags(session, flags_buf, sizeof(flags_buf)) /* "&nbsp;" */);
	 sendString(buf);

 #ifdef PARM_PRINT_ALL_SESSIONS
	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>"
		       "<TD "TD_BG" ALIGN=CENTER>%s</TD>",
		       getSessionState(session));
	 sendString(buf);
 #endif
	 sendString("</TR>\n");

	 session = session->next;
	 printedSessions++;
       }
     }
     
     releaseMutex(&myGlobals.tcpSessionsMutex);
   }

   if(printedSessions > 0) {
     sendString("</TABLE>"TABLE_OFF"<P>\n");
     sendString("</CENTER>\n");

     if(el == NULL)
       addPageIndicator(CONST_ACTIVE_TCP_SESSIONS_HTML, pageNum,
			myGlobals.device[actualDeviceId].numTcpSessions,
			myGlobals.runningPref.maxNumLines, -1, 0, -1);

     printHostColorCode(FALSE, 0);

     printFooterHostLink();
   } else {
     if(el == NULL) {
       printHTMLheader("Active TCP/UDP Sessions", NULL, 0);
       printNoDataYet();
     }
   }
 }

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

 void printIpProtocolUsage(void) {
   HostTraffic **hosts, *el;
   u_short clientPorts[MAX_ASSIGNED_IP_PORTS], serverPorts[MAX_ASSIGNED_IP_PORTS];
   u_int j, idx1, hostsNum=0, numPorts=0, maxHosts;
   char buf[LEN_GENERAL_WORK_BUFFER], portBuf[32], hostLinkBuf[LEN_GENERAL_WORK_BUFFER];
   PortUsage *ports;

   printHTMLheader("TCP/UDP: Local Protocol Usage", NULL, 0);

   memset(clientPorts, 0, sizeof(clientPorts));
   memset(serverPorts, 0, sizeof(serverPorts));

   hosts = (HostTraffic**)mallocAndInitWithReportWarn(myGlobals.device[myGlobals.actualReportDeviceId].
						      hostsno*sizeof(HostTraffic*),
						      "printIpProtocolUsage");
   if(hosts == NULL)
     return;

   maxHosts = myGlobals.device[myGlobals.actualReportDeviceId].hostsno;

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

     if(el->community && (!isAllowedCommunity(el->community))) continue;
     if(subnetPseudoLocalHost(el) && (el->hostNumIpAddress[0] != '\0')) {
       hosts[hostsNum++] = el;

       if(el->portsUsage != NULL) {
	 ports = el->portsUsage;
	 while(ports) {
	   j = ports->port;
	   if((clientPorts[j] == 0) && (serverPorts[j] == 0))
	     numPorts++;
	   clientPorts[j] += ports->clientUses;
	   serverPorts[j] += ports->serverUses;
	   ports = ports->next;
	 }
       }
     }

     if(hostsNum >= maxHosts) break;
   } /* for */

   if(numPorts == 0) {
     printNoDataYet();
     free(hosts);
     return;
   }

   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		 "<center><p>Reporting on actual traffic for %d host(s) on %d service port(s)</p></center>\n",
		 hostsNum, numPorts);
   sendString(buf);

   /* Hosts are now in a contiguous structure (hosts[])... */

   sendString("<CENTER>\n");
   sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "TR_ON" "DARK_BG"><TH "TH_BG" COLSPAN=2>Service</TH>"
	      "<TH "TH_BG">Clients</TH><TH "TH_BG">Servers</TH>\n");

   for(j=0; j<MAX_ASSIGNED_IP_PORTS; j++)
     if((clientPorts[j] > 0) || (serverPorts[j] > 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=CENTER>%d</TD>"
		     "<TD "TD_BG">\n", getRowColor(),
		     getAllPortByNum(j, portBuf, sizeof(portBuf)), j);
       sendString(buf);

       if(clientPorts[j] > 0) {
	 sendString("<UL>");
	 for(idx1=0; idx1<hostsNum; idx1++) {
	   ports = getPortsUsage(hosts[idx1], j, 0);
	   if((hosts[idx1]->portsUsage != NULL)
	      && ports && (ports->clientUses > 0)) {
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<li>%s\n",
			   makeHostLink(hosts[idx1], FLAG_HOSTLINK_TEXT_FORMAT, 1, 0, hostLinkBuf, sizeof(hostLinkBuf)));
	     sendString(buf);
	   }
	 }
	 sendString("</UL>");
       } else
	 sendString("&nbsp;");

       sendString("</TD><TD "TD_BG">");

       if(serverPorts[j] > 0) {
	 sendString("<UL>");
	 for(idx1=0; idx1<hostsNum; idx1++) {
	   ports = getPortsUsage(hosts[idx1], j, 0);
	   if((hosts[idx1]->portsUsage != NULL)
	      && ports && (ports->serverUses > 0)) {
	     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<li>%s\n",
			   makeHostLink(hosts[idx1], FLAG_HOSTLINK_TEXT_FORMAT, 1, 0, hostLinkBuf, sizeof(hostLinkBuf)));
	     sendString(buf);
	   }
	 }
	 sendString("</UL>");
       } else
	 sendString("&nbsp;");

       sendString("</TD></TR>");
     } /* for */

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

   printHostColorCode(FALSE, 0);

   printFooterHostLink();

   free(hosts);
 }

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

 void printBar(char *buf, int bufLen,
	       unsigned short percentageS, /* or the ONLY percentage if R = FLAG_NONSPLITBAR */
	       unsigned short percentageR,
	       unsigned short maxPercentage,
	       unsigned short ratio) {

   /* This shouldn't happen */
   if(maxPercentage > 100) { maxPercentage = 100; }

   if(percentageR == FLAG_NONSPLITBAR) {
     /* Single bar */
     if(percentageS > maxPercentage) { percentageS = maxPercentage; }

     switch(percentageS) {
     case 0:
       safe_snprintf(__FILE__, __LINE__, buf, bufLen, "<TD "TD_BG" %s>&nbsp;</TD>\n", getActualRowColor());
       break;
     default:
       safe_snprintf(__FILE__, __LINE__, buf, bufLen,
		     "<TD "TD_BG" ALIGN=LEFT>"
		     "<IMG ALIGN=ABSMIDDLE SRC=\"/gauge.jpg\" ALT=\"%d%%\" WIDTH=%d HEIGHT=12>"
		     "&nbsp;</TD>\n",
		     percentageS, ratio*percentageS);
       break;
     }
   } else {
     /* Could happen because of rounding */
     if((percentageS+percentageR) > maxPercentage)
       percentageR--;
     if((percentageS+percentageR) > maxPercentage)
       percentageS--;

     switch(percentageS+percentageR) {
     case 0:
       safe_snprintf(__FILE__, __LINE__, buf, bufLen, "<TD "TD_BG" %s>&nbsp;</TD>\n", getActualRowColor());
       break;
     default:
       safe_snprintf(__FILE__, __LINE__, buf, bufLen,
		     "<TD "TD_BG" ALIGN=LEFT>"
		     "<IMG ALIGN=ABSMIDDLE SRC=\"/gaugeS.jpg\" ALT=\"Sent %d%%\" WIDTH=%d HEIGHT=12>"
		     "<IMG ALIGN=ABSMIDDLE SRC=\"/gaugeR.jpg\" ALT=\"Received %d%%\" WIDTH=%d HEIGHT=12>"
		     "&nbsp;</TD>\n",
		     percentageS, ratio*percentageS, percentageR, ratio*percentageR);
       break;
     }
   }

   sendString(buf);
 }

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

 static int cmpPortsFctn(const void *_a, const void *_b) {
   if((_a == NULL) || (_b == NULL))
     return(0);
   else {
     PortCounter *a, *b;

     a = *((PortCounter**)_a);
     b = *((PortCounter**)_b);

     if((a == NULL) || (b == NULL))
       return(0);

     if((a->sent+a->rcvd) > (b->sent+b->rcvd))
       return(-1);
     else
       return(1);
   }
 }

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

 void printIpProtocolDistribution(int mode, int revertOrder, int printGraph) {
   int i;
   char buf[4*LEN_GENERAL_WORK_BUFFER], *sign;
   float total, partialTotal, remainingTraffic;
   float percentage;
   char formatBuf[32], formatBuf1[32], formatBuf2[32];

   if(revertOrder)
     sign = "";
   else
     sign = "-";

   if(mode == FLAG_HOSTLINK_TEXT_FORMAT) {
     printSectionTitle("IP Protocol Distribution");

 #ifndef EMBEDDED
     sendString("<TR "TR_ON" BGCOLOR=white><TH "TH_BG" ALIGN=CENTER COLSPAN=3 BGCOLOR=white>"
		"<iframe frameborder=0 SRC=\"" CONST_PIE_IPPROTO_RL_DIST  CHART_FORMAT "\" width=400 height=250></iframe></TH></TR>\n");
 #endif

     printSectionTitle("Local Traffic");

     total = (float)(myGlobals.device[myGlobals.actualReportDeviceId].tcpGlobalTrafficStats.local.value+
		     myGlobals.device[myGlobals.actualReportDeviceId].udpGlobalTrafficStats.local.value)/1024;
     if(total == 0)
       printNoDataYet();
     else {
       sendString("<CENTER>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"80%\"><TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG" WIDTH=150>IP&nbsp;Protocol</TH>"
		  "<TH "TH_BG" WIDTH=100>Data</TH><TH "TH_BG" WIDTH=250>"
		  "Percentage</TH></TR>\n");
       if(total == 0) total = 1; /* Avoids divisions by zero */
       remainingTraffic = 0;

       partialTotal = (float)myGlobals.device[myGlobals.actualReportDeviceId].tcpGlobalTrafficStats.local.value/1024;
       percentage = ((float)(partialTotal*100))/((float)total);
       printTableEntryPercentage(buf, sizeof(buf), "TCP&nbsp;vs.&nbsp;UDP",
				 "TCP", "UDP", total, percentage, 0, 0);

       sendString("</TABLE>"TABLE_OFF"<P>\n");
       sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"80%\"><TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG" WIDTH=150>TCP/UDP&nbsp;Protocol</TH>"
		  "<TH "TH_BG" WIDTH=100>Data</TH><TH "TH_BG" WIDTH=250 COLSPAN=2>"
		  "Percentage</TH></TR>\n");

       for(i=0; i<myGlobals.numIpProtosToMonitor; i++) {
	 partialTotal = (float)myGlobals.device[myGlobals.actualReportDeviceId].ipProtoStats[i].local.value/1024;

	 if(partialTotal > 0) {
	   remainingTraffic += partialTotal;
	   percentage = ((float)(partialTotal*100))/((float)total);
	   printTableEntry(buf, sizeof(buf), myGlobals.ipTrafficProtosNames[i],
			   CONST_COLOR_1, partialTotal, percentage, 0, 0, 0);
	 }
       }

       if(total > remainingTraffic)
	 remainingTraffic = total - remainingTraffic;
       else
	 remainingTraffic = 0;

       if(remainingTraffic > 0) {
	 percentage = ((float)(remainingTraffic*100))/((float)total);
	 printTableEntry(buf, sizeof(buf), "Other&nbsp;TCP/UDP-based&nbsp;Protocols",
			 CONST_COLOR_1, remainingTraffic, percentage, 0, 0, 0);
       }

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

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

     total = (float)(myGlobals.device[myGlobals.actualReportDeviceId].tcpGlobalTrafficStats.remote2local.value+
		     myGlobals.device[myGlobals.actualReportDeviceId].udpGlobalTrafficStats.remote2local.value)/1024;

     printSectionTitle("Remote to Local Traffic");

     if(total == 0)
       printNoDataYet();
     else {
       sendString("<CENTER>\n");
       sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"80%\"><TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG" WIDTH=150>IP&nbsp;Protocol</TH>"
		  "<TH "TH_BG" WIDTH=100>Data</TH><TH "TH_BG" WIDTH=250>"
		  "Percentage</TH></TR>\n");

       if(total == 0) total = 1; /* Avoids divisions by zero */
       remainingTraffic = 0;

       partialTotal = (float)myGlobals.device[myGlobals.actualReportDeviceId].tcpGlobalTrafficStats.remote2local.value/1024;
       percentage = ((float)(partialTotal*100))/((float)total);
       printTableEntryPercentage(buf, sizeof(buf), "TCP&nbsp;vs.&nbsp;UDP",
				 "TCP", "UDP", total, percentage, 0, 0);

       sendString("</TABLE>"TABLE_OFF);
       sendString("<P>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"80%\"><TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG" WIDTH=150>TCP/UDP&nbsp;Protocol</TH>"
		  "<TH "TH_BG" WIDTH=100>Data</TH><TH "TH_BG" WIDTH=250 COLSPAN=2>"
		  "Percentage</TH></TR>\n");

       for(i=0; i<myGlobals.numIpProtosToMonitor; i++) {
	 partialTotal = (float)myGlobals.device[myGlobals.actualReportDeviceId].ipProtoStats[i].remote2local.value/1024;

	 if(partialTotal > 0) {
	   remainingTraffic += partialTotal;
	   percentage = ((float)(partialTotal*100))/((float)total);
	   printTableEntry(buf, sizeof(buf), myGlobals.ipTrafficProtosNames[i],
			   CONST_COLOR_1, partialTotal, percentage, 0, 0, 0);
	 }
       }

       if(total > remainingTraffic)
	 remainingTraffic = total - remainingTraffic;
       else
	 remainingTraffic = 0;

       if(remainingTraffic > 0) {
	 percentage = ((float)(remainingTraffic*100))/((float)total);
	 printTableEntry(buf, sizeof(buf), "Other&nbsp;TCP/UDP-based&nbsp;Protocols",
			 CONST_COLOR_1, remainingTraffic, percentage, 0, 0, 0);
       }
       sendString("</TABLE>"TABLE_OFF"\n<P>\n");
       sendString("</CENTER>\n");
     }

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

     /* Courtesy of "Burton M. Strauss III" <BStrauss3@attbi.com> */

     printSectionTitle("Remote Traffic");

     total = (float)(myGlobals.device[myGlobals.actualReportDeviceId].tcpGlobalTrafficStats.remote.value+
		     myGlobals.device[myGlobals.actualReportDeviceId].udpGlobalTrafficStats.remote.value)/1024;
     if(total == 0)
       printNoDataYet();
     else {
       sendString("<CENTER>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"80%\"><TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG" WIDTH=150>IP&nbsp;Protocol</TH>"
		  "<TH "TH_BG" WIDTH=100>Data</TH><TH "TH_BG" WIDTH=250>"
		  "Percentage</TH></TR>\n");
       if(total == 0) total = 1; /* Avoids divisions by zero */
       remainingTraffic = 0;

       partialTotal = (float)myGlobals.device[myGlobals.actualReportDeviceId].tcpGlobalTrafficStats.remote.value/1024;
       percentage = ((float)(partialTotal*100))/((float)total);
       printTableEntryPercentage(buf, sizeof(buf), "TCP&nbsp;vs.&nbsp;UDP",
				 "TCP", "UDP", total, percentage, 0, 0);

       sendString("</TABLE>"TABLE_OFF"\n");
       sendString("<P>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"80%\"><TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG" WIDTH=150>TCP/UDP&nbsp;Protocol</TH>"
		  "<TH "TH_BG" WIDTH=100>Data</TH><TH "TH_BG" WIDTH=250 COLSPAN=2>"
		  "Percentage</TH></TR>\n");

       for(i=0; i<myGlobals.numIpProtosToMonitor; i++) {
	 partialTotal =
	   (float)myGlobals.device[myGlobals.actualReportDeviceId].ipProtoStats[i].remote.value/1024;

	 if(partialTotal > 0) {
	   remainingTraffic += partialTotal;
	   percentage = ((float)(partialTotal*100))/((float)total);
	   printTableEntry(buf, sizeof(buf),
			   myGlobals.ipTrafficProtosNames[i],
			   CONST_COLOR_1, partialTotal, percentage, 0, 0, 0);
	 }
       }

       if(total > remainingTraffic)
	 remainingTraffic = total - remainingTraffic;
       else
	 remainingTraffic = 0;

       if(remainingTraffic > 0) {
	 percentage = ((float)(remainingTraffic*100))/((float)total);
	 printTableEntry(buf, sizeof(buf),
			 "Other&nbsp;TCP/UDP-based&nbsp;Protocols",
			 CONST_COLOR_1, remainingTraffic, percentage, 0, 0, 0);
       }

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

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

     printSectionTitle("Local to Remote Traffic");

     total = (float)(myGlobals.device[myGlobals.actualReportDeviceId].tcpGlobalTrafficStats.local2remote.value+
		     myGlobals.device[myGlobals.actualReportDeviceId].udpGlobalTrafficStats.local2remote.value)/1024;
     if(total == 0)
       printNoDataYet();
     else {
       sendString("<CENTER>\n");
       sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"80%\"><TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG" WIDTH=150>IP&nbsp;Protocol</TH>"
		  "<TH "TH_BG" WIDTH=100>Data</TH>"
		  "<TH "TH_BG" WIDTH=250>Percentage</TH></TR>\n");

       if(total == 0) total = 1; /* Avoids divisions by zero */
       remainingTraffic = 0;

       partialTotal = (float)myGlobals.device[myGlobals.actualReportDeviceId].
	 tcpGlobalTrafficStats.local2remote.value/1024;
       percentage = ((float)(partialTotal*100))/((float)total);
       printTableEntryPercentage(buf, sizeof(buf), "TCP&nbsp;vs.&nbsp;UDP",
				 "TCP", "UDP", total, percentage, 0, 0);

       sendString("</TABLE>"TABLE_OFF);
       sendString("<P>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"80%\"><TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG" WIDTH=150>TCP/UDP&nbsp;Protocol</TH>"
		  "<TH "TH_BG" WIDTH=100>Data</TH>"
		  "<TH "TH_BG" WIDTH=250 COLSPAN=2>Percentage</TH></TR>\n");

       for(i=0; i<myGlobals.numIpProtosToMonitor; i++) {
	 partialTotal = (float)myGlobals.device[myGlobals.actualReportDeviceId].
	   ipProtoStats[i].local2remote.value/1024;

	 if(partialTotal > 0) {
	   remainingTraffic += partialTotal;
	   percentage = ((float)(partialTotal*100))/((float)total);
	   printTableEntry(buf, sizeof(buf), myGlobals.ipTrafficProtosNames[i],
			   CONST_COLOR_1, partialTotal, percentage, 0, 0, 0);
	 }
       }

       if(total > remainingTraffic)
	 remainingTraffic = total - remainingTraffic;
       else
	 remainingTraffic = 0;

       if(remainingTraffic > 0) {
	 percentage = ((float)(remainingTraffic*100))/((float)total);
	 printTableEntry(buf, sizeof(buf), "Other&nbsp;IP-based&nbsp;Protocols",
			 CONST_COLOR_1, remainingTraffic, percentage, 0, 0, 0);
       }
       sendString("</TABLE>"TABLE_OFF"<P>\n");
       sendString("</CENTER>\n");
     }
   } else {
     total = (float)myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value;

     if(myGlobals.device[myGlobals.actualReportDeviceId].ipProtosList) {
       ProtocolsList *protoList = myGlobals.ipProtosList;
       int idx1 = 0;

       while(protoList != NULL) {
	 if(total > (float)myGlobals.device[myGlobals.actualReportDeviceId].ipProtosList[idx1].value)
	   total -= (float)myGlobals.device[myGlobals.actualReportDeviceId].ipProtosList[idx1].value;
	 else
	   total = 0;

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

     if(total == 0)
       return;
     else {
       int numProtosFound = 0;
       float remainingFlows = 0;

       printSectionTitle("Global TCP/UDP Protocol Distribution");

       sendString("<CENTER>\n");
       sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">"
		  "<TR "TR_ON" "DARK_BG"><TH "TH_BG" WIDTH=150>"
		  "TCP/UDP&nbsp;Protocol</TH>"
		  "<TH "TH_BG" WIDTH=50>Data</TH><TH "TH_BG">Flows</TH><TH "TH_BG" COLSPAN=2 width=260>"
		  "Accumulated Percentage / Historical Protocol View</TH></TR>\n");

       remainingTraffic = 0;

       for(i=0; i<myGlobals.numIpProtosToMonitor; i++) {
	 partialTotal  = (float)myGlobals.device[myGlobals.actualReportDeviceId].ipProtoStats[i].local.value
	   +myGlobals.device[myGlobals.actualReportDeviceId].ipProtoStats[i].remote.value;
	 partialTotal += (float)myGlobals.device[myGlobals.actualReportDeviceId].ipProtoStats[i].remote2local.value
	   +myGlobals.device[myGlobals.actualReportDeviceId].ipProtoStats[i].local2remote.value;
	 remainingFlows += (float)myGlobals.device[myGlobals.actualReportDeviceId].ipProtoStats[i].totalFlows.value;

	 if(partialTotal > 0) {
	   remainingTraffic += partialTotal;
	   percentage = ((float)(partialTotal*100))/((float)total);
	   numProtosFound++;
	   printTableEntry(buf, sizeof(buf),
			   myGlobals.ipTrafficProtosNames[i], CONST_COLOR_1,
			   partialTotal/1024, percentage,
			   1, myGlobals.device[myGlobals.actualReportDeviceId].ipProtoStats[i].totalFlows.value, 1);
	 }
       }

       if(total > remainingTraffic)
	 remainingTraffic = total - remainingTraffic;
       else
	 remainingTraffic = 0;

       if(remainingTraffic > 0) {
	 float totFlows = (float)myGlobals.device[myGlobals.actualReportDeviceId].udpGlobalTrafficStats.totalFlows.value +
	   (float)myGlobals.device[myGlobals.actualReportDeviceId].tcpGlobalTrafficStats.totalFlows.value;

	 if(remainingFlows > totFlows)
	   remainingFlows = 0;
	 else
	   remainingFlows = totFlows - remainingFlows;

	 percentage = ((float)(remainingTraffic*100))/((float)total);
	 printTableEntry(buf, sizeof(buf), "Other&nbsp;TCP/UDP-based&nbsp;Protocols",
			 CONST_COLOR_1, remainingTraffic/1024, percentage,
			 1, remainingFlows, 0);
       }

 #ifndef EMBEDDED
	   if((numProtosFound > 0) && printGraph) {
		   struct stat statbuf;

		   if(strcmp(myGlobals.device[0].name, "pcap-file")) {
			   sendString("<TR "TR_ON" "DARK_BG"><TH "TH_BG" "DARK_BG">Accumulated View</TH><TD "TD_BG" COLSPAN=4 ALIGN=LEFT BGCOLOR=white>"
				   "<iframe  frameborder=0 SRC=\"" CONST_BAR_IPPROTO_DIST  CHART_FORMAT "\" width=400 height=250></iframe></TH></TR>\n");

			   /* RRD */
			   /* Do NOT add a '/' at the end of the path because Win32 will complain about it */
			   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s/interfaces/%s",
				   myGlobals.rrdPath != NULL ? myGlobals.rrdPath : ".",
				   myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName);

			   revertSlashIfWIN32(buf, 0);

			   if((i = stat(buf, &statbuf)) == 0) {
				   time_t now = time(NULL);
				   u_char found = 0;

				   /* We need to check whether there are interesting rrd files in the directory */
				   DIR* directoryPointer = opendir(buf);

				   if(directoryPointer != NULL) {
					   struct dirent* dp;

					   i = 0;

					   while((dp = readdir(directoryPointer)) != NULL) {
						   int len = strlen(dp->d_name);

						   if(dp->d_name[0] == '.') continue;
						   else if(len < 7 /* IP_ + .rrd */ ) continue;
						   else if(strncmp(dp->d_name, "IP_", 3)) continue;
						   else if(strstr(dp->d_name, "Flows")) continue;
						   else found = 1;
					   }
				   }

				   if(found) {
					   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
						   "<TR "TR_ON" "DARK_BG"><TH "TH_BG" "DARK_BG">Historical View</TH><TD "TD_BG" COLSPAN=4 ALIGN=left BGCOLOR=white>"
						   "<table border=0><tr><td><IMG SRC=\"/plugins/rrdPlugin?action=graphSummary&graphId=4&"
						   "key=interfaces/%s/&start=now-12h&end=now\" BORDER=0>",
						   myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName);
					   sendString(buf);

					   safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
						   "</td><td><A HREF=\"/plugins/rrdPlugin?mode=zoom&action=graphSummary&graphId=4&"
						   "key=interfaces/%s/&start=%u&end=%u\"><IMG valign=middle class=tooltip SRC=/graph_zoom.gif border=0></A></tr></table></TD></TR>",
						   myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, (u_int)(now - 12 * 3600), (u_int)now);
					   sendString(buf);
				   }

				   sendString("</TD></TR>\n");
			   }
		   }
	   }
 #endif
       sendString("<TR "TR_ON"><TD "TD_BG" COLSPAN=5 ALIGN=LEFT "DARK_BG">"
		  "Note:\n"
		  "<ul><li>What is a flow?<br><ul><li>TCP: a flows is a TCP connection."
		  "<li>UDP: a flow is a set of packets with the same protocol/peers/port.</ul>"
		  "<li>TCP flows are not accounted for fully (sender and recipient) remote peers."
		  "</ul>"
		  "</TD></TR>\n");
       sendString("</TABLE>"TABLE_OFF"<P>\n");

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

       if((remainingTraffic > 0) && printGraph) {
	 PortCounter **ipPorts;
	 int idx = 0;

	 ipPorts = (PortCounter**)calloc(MAX_IP_PORT, sizeof(PortCounter*));

	 for(i=0; i<MAX_IP_PORT; i++) {
	   if(myGlobals.device[myGlobals.actualReportDeviceId].ipPorts[i] != NULL) {
	     ipPorts[idx] = myGlobals.device[myGlobals.actualReportDeviceId].ipPorts[i];
	     idx++;
	   }
	 }

	 if(idx > 0) {
	   printSectionTitle("TCP/UDP Traffic Port Distribution:<br>Last Minute View");

	   sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "TR_ON" "DARK_BG">"
		      "<TH "TH_BG" colspan=2>TCP/UDP Port</TH>"
		      "<TH "TH_BG">Total</TH><TH "TH_BG">Sent</TH><TH "TH_BG">Rcvd</TH></TR>");

	   qsort(ipPorts, idx, sizeof(PortCounter**), cmpPortsFctn);

	   if(idx > 32) idx = 32; /* Limit to 32 entries max */

	   for(i=0; i<idx; i++) {
	     if(ipPorts[i] != NULL) {
	       char portBuf[32];
	       char *symPort = getAllPortByNum(ipPorts[i]->port, portBuf, sizeof(portBuf));

	       if(symPort == NULL) symPort = "";

	       safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>"
			     "<TH "TH_BG" ALIGN=LEFT><A HREF=\"" CONST_SHOW_PORT_TRAFFIC_HTML "?port=%d\">%s</A>"
			     "</th><td align=right>%d</td>"
			     "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			     "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			     "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
			     "</TR>\n",
			     getRowColor(),
			     ipPorts[i]->port, symPort, ipPorts[i]->port,
			     formatBytes(ipPorts[i]->sent+ipPorts[i]->rcvd, 1, formatBuf, sizeof(formatBuf)),
			     formatBytes(ipPorts[i]->sent, 1, formatBuf1, sizeof(formatBuf1)),
			     formatBytes(ipPorts[i]->rcvd, 1, formatBuf2, sizeof(formatBuf2)));
	       sendString(buf);
	     }
	   } /* for */

	   sendString("<tr><td align=left "DARK_BG" colspan=5>Notes:<ul>"
		      "<li>sum(total traffic per port) = 2*(total IP traffic)"
		      "<br>because the traffic per port is counted twice (sent and received)"
		      "<li>This report includes broadcast packets</ul></td></tr>\n");
	 }
	 free(ipPorts);
	 sendString("</TABLE>"TABLE_OFF"<P></center>\n");
	 if(idx >= 32)
	   sendString("<p>This extract is just a sample of the packets ntop has seen.</p>");
       } else {
	 sendString("<p>Note: This report includes broadcast packets.</p>\n");
	 sendString("</CENTER>\n");
       }
     }
   }
 }

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

 void printProtoTraffic(int printGraph) {
   float total, perc;
   char buf[2*LEN_GENERAL_WORK_BUFFER], formatBuf[32];

   total = myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value/1024; /* total is expressed in KBytes.value */

   if(total == 0)
     return;

   sendString("<CENTER>\n");

   if(myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value > 0) {
     printSectionTitle("Global Protocol Distribution");

     sendString("<P>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "TR_ON" "DARK_BG"><TH "TH_BG" WIDTH=150>Protocol</TH>"
		"<TH "TH_BG" WIDTH=50>Data</TH><TH "TH_BG" WIDTH=250 COLSPAN=2>Percentage</TH></TR>\n");

     perc = 100*((float)myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value/
		 myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value);
     if(perc > 100) perc = 100;

     safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" WIDTH=150 ALIGN=LEFT "DARK_BG">IP</TH>"
		   "<TD "TD_BG" WIDTH=50 ALIGN=RIGHT>%s"
		   "</td><td align=right WIDTH=50>%.1f%%</TD><TD "TD_BG" WIDTH=200>"
		   "<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"100%%\">",
		   getRowColor(),
		   formatBytes(myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value, 1,
			       formatBuf, sizeof(formatBuf)),
		   perc);
     sendString(buf);

     printTableEntry(buf, sizeof(buf), "TCP", CONST_COLOR_1,
		     (float)myGlobals.device[myGlobals.actualReportDeviceId].tcpBytes.value/1024,
		     100*((float)myGlobals.device[myGlobals.actualReportDeviceId].tcpBytes.value/
			  myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value), 0, 0, 0);
     printTableEntry(buf, sizeof(buf), "UDP", CONST_COLOR_1,
		     (float)myGlobals.device[myGlobals.actualReportDeviceId].udpBytes.value/1024,
		     100*((float)myGlobals.device[myGlobals.actualReportDeviceId].udpBytes.value/
			  myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value), 0, 0, 0);
     printTableEntry(buf, sizeof(buf), "ICMP", CONST_COLOR_1,
		     (float)myGlobals.device[myGlobals.actualReportDeviceId].icmpBytes.value/1024,
		     100*((float)myGlobals.device[myGlobals.actualReportDeviceId].icmpBytes.value/
			  myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value), 0, 0, 0);
     printTableEntry(buf, sizeof(buf), "ICMPv6", CONST_COLOR_1,
		     (float)myGlobals.device[myGlobals.actualReportDeviceId].icmp6Bytes.value/1024,
		     100*((float)myGlobals.device[myGlobals.actualReportDeviceId].icmp6Bytes.value/
			  myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value), 0, 0, 0);

     if(myGlobals.device[myGlobals.actualReportDeviceId].ipProtosList) {
       ProtocolsList *protoList = myGlobals.ipProtosList;
       int idx = 0;

       while(protoList != NULL) {
	 printTableEntry(buf, sizeof(buf), protoList->protocolName, CONST_COLOR_1,
			 (float)myGlobals.device[myGlobals.actualReportDeviceId].ipProtosList[idx].value/1024,
			 100*((float)myGlobals.device[myGlobals.actualReportDeviceId].ipProtosList[idx].value/
			      myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value), 0, 0, 0);
	 idx++, protoList = protoList->next;
       }
     }

     printTableEntry(buf, sizeof(buf), "Other&nbsp;IP", CONST_COLOR_1,
		     (float)myGlobals.device[myGlobals.actualReportDeviceId].otherIpBytes.value/1024,
		     ((float)myGlobals.device[myGlobals.actualReportDeviceId].otherIpBytes.value/
		      myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value), 0, 0, 0);

     sendString("</TABLE>"TABLE_OFF"</TR>");
   } else
     printGraph = 0;

   printTableEntry(buf, sizeof(buf), "(R)ARP", CONST_COLOR_1,
		   (float)myGlobals.device[myGlobals.actualReportDeviceId].arpRarpBytes.value/1024,
		   100*((float)myGlobals.device[myGlobals.actualReportDeviceId].arpRarpBytes.value/
			myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value), 0, 0, 0);
   printTableEntry(buf, sizeof(buf), "DLC", CONST_COLOR_1,
		   (float)myGlobals.device[myGlobals.actualReportDeviceId].dlcBytes.value/1024,
		   100*((float)myGlobals.device[myGlobals.actualReportDeviceId].dlcBytes.value/
			myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value), 0, 0, 0);
   printTableEntry(buf, sizeof(buf), "IPX", CONST_COLOR_1,
		   (float)myGlobals.device[myGlobals.actualReportDeviceId].ipxBytes.value/1024,
		   100*((float)myGlobals.device[myGlobals.actualReportDeviceId].ipxBytes.value/
			myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value), 0, 0, 0);
   printTableEntry(buf, sizeof(buf), "Decnet", CONST_COLOR_1,
		   (float)myGlobals.device[myGlobals.actualReportDeviceId].decnetBytes.value/1024,
		   100*((float)myGlobals.device[myGlobals.actualReportDeviceId].decnetBytes.value/
			myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value), 0, 0, 0);
   printTableEntry(buf, sizeof(buf), "AppleTalk", CONST_COLOR_1,
		   (float)myGlobals.device[myGlobals.actualReportDeviceId].atalkBytes.value/1024,
		   100*((float)myGlobals.device[myGlobals.actualReportDeviceId].atalkBytes.value/
			myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value), 0, 0, 0);
   printTableEntry(buf, sizeof(buf), "NetBios", CONST_COLOR_1,
		   (float)myGlobals.device[myGlobals.actualReportDeviceId].netbiosBytes.value/1024,
		   100*((float)myGlobals.device[myGlobals.actualReportDeviceId].netbiosBytes.value/
			myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value), 0, 0, 0);
   printTableEntry(buf, sizeof(buf), "OSI", CONST_COLOR_1,
		   (float)myGlobals.device[myGlobals.actualReportDeviceId].osiBytes.value/1024,
		   100*((float)myGlobals.device[myGlobals.actualReportDeviceId].osiBytes.value/
			myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value), 0, 0, 0);
   printTableEntry(buf, sizeof(buf), "IPv6", CONST_COLOR_1,
		   (float)myGlobals.device[myGlobals.actualReportDeviceId].ipv6Bytes.value/1024,
		   100*((float)myGlobals.device[myGlobals.actualReportDeviceId].ipv6Bytes.value/
			myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value), 0, 0, 0);
   printTableEntry(buf, sizeof(buf), "STP", CONST_COLOR_1,
		   (float)myGlobals.device[myGlobals.actualReportDeviceId].stpBytes.value/1024,
		   100*((float)myGlobals.device[myGlobals.actualReportDeviceId].stpBytes.value/
			myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value), 0, 0, 0);
   printTableEntry(buf, sizeof(buf), "Other", CONST_COLOR_1,
		   (float)myGlobals.device[myGlobals.actualReportDeviceId].otherBytes.value/1024,
		   100*((float)myGlobals.device[myGlobals.actualReportDeviceId].otherBytes.value/
			myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value), 0, 0, 0);
   
#ifndef EMBEDDED
   if(printGraph) {
     sendString("<TR "TR_ON"><TD "TD_BG" COLSPAN=4 ALIGN=CENTER BGCOLOR=white>"
		"<iframe frameborder=0 SRC=\"" CONST_BAR_ALLPROTO_DIST  CHART_FORMAT "\" "
		"width=650 height=250></iframe>"
		"</TD></TR>\n");
  }
#endif

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

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

void printIpTrafficMatrix(void) {
  int i, j, numEntries=0, numConsecutiveEmptyCells;
  char buf[LEN_GENERAL_WORK_BUFFER], formatBuf[32], hostLinkBuf[LEN_GENERAL_WORK_BUFFER];
  short *activeHosts;
  Counter minTraffic=(Counter)LONG_MAX, maxTraffic=0, avgTraffic;
  Counter avgTrafficLow, avgTrafficHigh, tmpCounter;

  printHTMLheader("IP Subnet Traffic Matrix", NULL, 0);

  if(myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix == NULL) {
    printFlagedWarning("<I>Traffic matrix is not available for the selected network interface</I>");
    return;
  }

  activeHosts = (short*)mallocAndInitWithReportWarn(myGlobals.device[myGlobals.actualReportDeviceId].numHosts*sizeof(short), "printIpTrafficMatrix");
  if(activeHosts == NULL)
    return;

  for(i=0; i<myGlobals.device[myGlobals.actualReportDeviceId].numHosts-1; i++) {
    activeHosts[i] = 0;

    for(j=0; j<myGlobals.device[myGlobals.actualReportDeviceId].numHosts-1; j++) {
      int id = i*myGlobals.device[myGlobals.actualReportDeviceId].numHosts+j;

      if(((myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[id] != NULL)
	  && (myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[id]->bytesSent.value != 0))
	 || ((myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[id] != NULL)
	     && (myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[id]->bytesRcvd.value != 0))) {
	activeHosts[i] = 1;
	numEntries++;
	break;
      }
    }

    if(activeHosts[i] == 1) {
      if(numEntries == 1) {
	sendString("<CENTER>\n");
	sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "TR_ON"><TH "TH_BG" ALIGN=LEFT "DARK_BG"><SMALL>&nbsp;F&nbsp;"
		   "&nbsp;&nbsp;To<br>&nbsp;r<br>&nbsp;o<br>&nbsp;m</SMALL></TH>\n");
      }

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TH "TH_BG" ALIGN=CENTER "DARK_BG"><SMALL>%s</SMALL></TH>",
		    getHostName(myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrixHosts[i],
				1, hostLinkBuf, sizeof(hostLinkBuf)));
      sendString(buf);
    }
  }

  if(numEntries == 0) {
    printNoDataYet();
    free(activeHosts);
    return;
  } else
    sendString("</TR>\n");

  for(i=0; i<myGlobals.device[myGlobals.actualReportDeviceId].numHosts-1; i++)
    for(j=0; j<myGlobals.device[myGlobals.actualReportDeviceId].numHosts-1; j++) {
      int idx = i*myGlobals.device[myGlobals.actualReportDeviceId].numHosts+j;

      if(((myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx] != NULL)
	  && ((myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx]->bytesSent.value != 0)
	      || (myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx]->bytesRcvd.value != 0)))) {
	if(minTraffic > myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx]->bytesSent.value)
	  minTraffic = myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx]->bytesSent.value;
	if(minTraffic > myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx]->bytesRcvd.value)
	  minTraffic = myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx]->bytesRcvd.value;
	if(maxTraffic < myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx]->bytesSent.value)
	  maxTraffic = myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx]->bytesSent.value;
	if(maxTraffic < myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx]->bytesRcvd.value)
	  maxTraffic = myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx]->bytesRcvd.value;
      }
    }

  avgTraffic = (Counter)(((float)minTraffic+(float)maxTraffic)/2);
  avgTrafficLow  = (avgTraffic*15)/100; /* 15% of the average */
  avgTrafficHigh = 2*(maxTraffic/3);   /* 75% of max traffic */


  for(i=0; i<myGlobals.device[myGlobals.actualReportDeviceId].numHosts; i++)
    if(activeHosts[i] == 1) {
      numConsecutiveEmptyCells=0;

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

      sendString(makeHostLink(myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrixHosts[i],
			      FLAG_HOSTLINK_TEXT_FORMAT, 1, 0, hostLinkBuf, sizeof(hostLinkBuf)));
      sendString("</SMALL></TH>");

      for(j=0; j<myGlobals.device[myGlobals.actualReportDeviceId].numHosts; j++) {
	int idx = i*myGlobals.device[myGlobals.actualReportDeviceId].numHosts+j;

	if((i == j) &&
	   strcmp(myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrixHosts[i]->hostNumIpAddress,
		  "127.0.0.1"))
	  numConsecutiveEmptyCells++;
	else if(activeHosts[j] == 1) {
	  if(myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx] == NULL)
	    numConsecutiveEmptyCells++;
	  else {
	    if(numConsecutiveEmptyCells > 0) {
	      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" COLSPAN=%d>&nbsp;</TD>\n",
			    numConsecutiveEmptyCells);
	      sendString(buf);
	      numConsecutiveEmptyCells = 0;
	    }

	    tmpCounter = myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx]->bytesSent.value+
	      myGlobals.device[myGlobals.actualReportDeviceId].ipTrafficMatrix[idx]->bytesRcvd.value;
	    /* Fix below courtesy of Danijel Doriae <danijel.doric@industrogradnja.tel.hr> */
	    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=CENTER %s>"
			  "<A HREF=# onMouseOver=\"window.status='"
			  "%s';return true\" onMouseOut="
			  "\"window.status='';return true\"><SMALL>%s</SMALL></A></TH>\n",
			  calculateCellColor(tmpCounter, avgTrafficLow, avgTrafficHigh),
			  buildHTMLBrowserWindowsLabel(i, j, TRUE),
			  formatBytes(tmpCounter, 1, formatBuf, sizeof(formatBuf)));
	    sendString(buf);
	  }
	}
      }

      if(numConsecutiveEmptyCells > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" COLSPAN=%d>&nbsp;</TD>\n",
		      numConsecutiveEmptyCells);
	sendString(buf);
	numConsecutiveEmptyCells = 0;
      }

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

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

  printFooterHostLink();

  free(activeHosts);
}

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

void printThptStatsMatrix(int sortedColumn) {
  int i, dataSent;
  char label[32], label1[32], buf[LEN_GENERAL_WORK_BUFFER],
    formatBuf[32], hostLinkBuf[LEN_GENERAL_WORK_BUFFER];
  time_t tmpTime;
  struct tm t;
  HostTraffic *el;
  HostTraffic tmpEl;

  printHTMLheader("Network Load Statistics Matrix", NULL, 0);

  memset (&tmpEl, 0, sizeof(HostTraffic));
  if(allocFcScsiCounters(&tmpEl) == NULL) {
    traceEvent (CONST_TRACE_WARNING, "Unable to allocate memory for FC counters");
    return;
  }

  switch(sortedColumn) {
  case 0:
  case 1:
    if(myGlobals.device[myGlobals.actualReportDeviceId].numThptSamples < 1) {
      printNoDataYet();
      free(tmpEl.fcCounters);
      return;
    }

    sendString("<CENTER>\n");
    sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR "TR_ON" "DARK_BG">"
	       "<TH "TH_BG">Sampling Period</TH>"
	       "<TH "TH_BG">Average Thpt</TH>"
	       "<TH "TH_BG">Top Hosts Sent Thpt</TH>"
	       "<TH "TH_BG">Top Hosts Rcvd Thpt</TH></TR>\n");

    for(i=0; i<(sortedColumn == 0 ? 10 : 60); i++) {
      if(myGlobals.device[myGlobals.actualReportDeviceId].last60MinutesThpt[i].trafficValue == 0)
	break;

      if(myGlobals.runningPref.rFileName != NULL) {
	tmpTime = myGlobals.lastPktTime.tv_sec-(i*60);
      }
      else {
	tmpTime = myGlobals.actTime-(i*60);
      }
      strftime(label, sizeof(label), CONST_TOD_NOSEC_TIMESPEC, localtime_r(&tmpTime, &t));

      if(myGlobals.runningPref.rFileName != NULL) {
	tmpTime = myGlobals.lastPktTime.tv_sec -((i+1)*60);
      }
      else {
	tmpTime = myGlobals.actTime-((i+1)*60);
      }
      strftime(label1, sizeof(label), CONST_TOD_NOSEC_TIMESPEC, localtime_r(&tmpTime, &t));
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TH "TH_BG" ALIGN=CENTER>"
		    "<B>%s&nbsp;-&nbsp;%s</B></TH>"
		    "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=LEFT>"
		    "<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=100%%>",
		    getRowColor(), label1, label,
		    formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
				     last60MinutesThpt[i].trafficValue, 1,
				     formatBuf, sizeof(formatBuf)));
      sendString(buf);

      dataSent = 0;

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

      if(!emptySerial(&myGlobals.device[myGlobals.actualReportDeviceId].last60MinutesThpt[i].topHostSentSerial)) {


	if((el = quickHostLink(myGlobals.device[myGlobals.actualReportDeviceId].
			       last60MinutesThpt[i].topHostSentSerial, myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
					 last60MinutesThpt[i].topSentTraffic.value, 1,
					 formatBuf, sizeof(formatBuf)));
	  sendString(buf); dataSent = 1;
	}
      }

      if(!emptySerial(&myGlobals.device[myGlobals.actualReportDeviceId].last60MinutesThpt[i].secondHostSentSerial)) {

	if((el = quickHostLink(myGlobals.device[myGlobals.actualReportDeviceId].
			       last60MinutesThpt[i].secondHostSentSerial, myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
					 last60MinutesThpt[i].secondSentTraffic.value, 1,
					 formatBuf, sizeof(formatBuf)));
	  sendString(buf); dataSent = 1;
	}
      }

      if(!emptySerial(&myGlobals.device[myGlobals.actualReportDeviceId].last60MinutesThpt[i].thirdHostSentSerial)) {

	if((el = quickHostLink(myGlobals.device[myGlobals.actualReportDeviceId].
			       last60MinutesThpt[i].thirdHostSentSerial, myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
					 last60MinutesThpt[i].thirdSentTraffic.value, 1,
					 formatBuf, sizeof(formatBuf)));
	  sendString(buf); dataSent = 1;
	}
      }

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

      if(!dataSent) sendString("&nbsp;");
      sendString("</TABLE>"TABLE_OFF"</TD><TD "TD_BG" ALIGN=LEFT><TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=100%%>\n");
      dataSent = 0;

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

      if(!emptySerial(&myGlobals.device[myGlobals.actualReportDeviceId].last60MinutesThpt[i].topHostRcvdSerial)) {

	if((el = quickHostLink(myGlobals.device[myGlobals.actualReportDeviceId].
			       last60MinutesThpt[i].topHostRcvdSerial, myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
					 last60MinutesThpt[i].topRcvdTraffic.value, 1,
					 formatBuf, sizeof(formatBuf)));
	  sendString(buf); dataSent = 1;
	}
      }

      if(!emptySerial(&myGlobals.device[myGlobals.actualReportDeviceId].last60MinutesThpt[i].secondHostRcvdSerial)) {

	if((el = quickHostLink(myGlobals.device[myGlobals.actualReportDeviceId].
			       last60MinutesThpt[i].secondHostRcvdSerial, myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
					 last60MinutesThpt[i].secondRcvdTraffic.value, 1,
					 formatBuf, sizeof(formatBuf)));
	  sendString(buf); dataSent = 1;
	}
      }

      if(!emptySerial(&myGlobals.device[myGlobals.actualReportDeviceId].last60MinutesThpt[i].thirdHostRcvdSerial)) {

	if((el = quickHostLink(myGlobals.device[myGlobals.actualReportDeviceId].
			       last60MinutesThpt[i].thirdHostRcvdSerial, myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
					 last60MinutesThpt[i].thirdRcvdTraffic.value, 1,
					 formatBuf, sizeof(formatBuf)));
	  sendString(buf); dataSent = 1;
	}
      }

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

      if(!dataSent) sendString("&nbsp;");
      sendString("</TABLE></TD></TR>\n");
    }
    break;
  case 2:
  default:
    if(myGlobals.device[myGlobals.actualReportDeviceId].numThptSamples < 60) {
      printNoDataYet();
      free (tmpEl.fcCounters);
      return;
    } else {
      sendString("<CENTER>\n");
      sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR "TR_ON">"
		 "<TH "TH_BG">Sampling Period</TH><TH "TH_BG">Average Thpt</TH>"
		 "<TH "TH_BG">Top Thpt Sent Hosts</TH><TH "TH_BG">Top Rcvd Sent Hosts</TH>"
		 "</TR>\n");

      for(i=0; i<24; i++) {
	if(myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].trafficValue == 0)
	  break;

        if(myGlobals.runningPref.rFileName != NULL) {
	  tmpTime = myGlobals.lastPktTime.tv_sec-(i*60*60);
        }
        else {
	  tmpTime = myGlobals.actTime-(i*60*60);
        }
	strftime(label, sizeof(label), CONST_TOD_NOSEC_TIMESPEC, localtime_r(&tmpTime, &t));

        if(myGlobals.runningPref.rFileName != NULL) {
	  tmpTime = myGlobals.lastPktTime.tv_sec-((i+1)*60*60);
        }
        else {
	  tmpTime = myGlobals.actTime-((i+1)*60*60);
        }
	strftime(label1, sizeof(label1), CONST_TOD_NOSEC_TIMESPEC, localtime_r(&tmpTime, &t));

	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s><TD "TD_BG" ALIGN=CENTER><B>%s&nbsp;-&nbsp;%s</B></TH>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=LEFT "DARK_BG">"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">",
		      getRowColor(), label, label1,
		      formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
				       last24HoursThpt[i].trafficValue, 1,
				       formatBuf, sizeof(formatBuf)));
	sendString(buf);

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

	if(!emptySerial(&myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].topHostSentSerial)) {

	  if((el = quickHostLink(myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].topHostSentSerial,
				 myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			  makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			  formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
					   last24HoursThpt[i].topSentTraffic.value, 1,
					   formatBuf, sizeof(formatBuf)));
	    sendString(buf);
	  }
	}

	if(!emptySerial(&myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].secondHostSentSerial)) {

	  if((el = quickHostLink(myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].secondHostSentSerial,
				 myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			  makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			  formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
					   last24HoursThpt[i].secondSentTraffic.value, 1,
					   formatBuf, sizeof(formatBuf)));
	    sendString(buf);
	  }
	}

	if(!emptySerial(&myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].thirdHostSentSerial)) {

	  if((el = quickHostLink(myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].thirdHostSentSerial,
				 myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			  makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			  formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
					   last24HoursThpt[i].thirdSentTraffic.value, 1,
					   formatBuf, sizeof(formatBuf)));
	    sendString(buf);
	  }
	}

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

	sendString("&nbsp;");
	sendString("</TABLE>"TABLE_OFF"</TD><TD "TD_BG" ALIGN=LEFT "DARK_BG">"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n");

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

	if(!emptySerial(&myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].topHostRcvdSerial)) {

	  if((el = quickHostLink(myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].topHostRcvdSerial,
				 myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			  makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			  formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
					   last24HoursThpt[i].topRcvdTraffic.value, 1,
					   formatBuf, sizeof(formatBuf)));
	    sendString(buf);
	  }
	}

	if(!emptySerial(&myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].secondHostRcvdSerial)) {

	  if((el = quickHostLink(myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].secondHostRcvdSerial,
				 myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			  makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			  formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
					   last24HoursThpt[i].secondRcvdTraffic.value, 1,
					   formatBuf, sizeof(formatBuf)));
	    sendString(buf);
	  }
	}

	if(!emptySerial(&myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].thirdHostRcvdSerial)) {

	  if((el = quickHostLink(myGlobals.device[myGlobals.actualReportDeviceId].last24HoursThpt[i].thirdHostRcvdSerial,
				 myGlobals.actualReportDeviceId, &tmpEl)) != NULL) {
	    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON">%s<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
			  makeHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)),
			  formatThroughput(myGlobals.device[myGlobals.actualReportDeviceId].
					   last24HoursThpt[i].thirdRcvdTraffic.value, 1,
					   formatBuf, sizeof(formatBuf)));
	    sendString(buf);
	  }
	}

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

	sendString("&nbsp;");
	sendString("</TABLE>"TABLE_OFF"</TD></TR>\n");
      }
    }
    break;
  }

  sendString("</TABLE>"TABLE_OFF"</CENTER>\n");
  free (tmpEl.fcCounters);
}

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

#define RRD_THPT_URL "/plugins/rrdPlugin?action=arbreq&which=graph&arbfile=throughput&arbiface=%s&arbip=&start=%u&end=%u&counter=&title=%s"

#define RRD_THPT_STR "<tr><td align=right><A HREF=\"" CONST_THPT_STATS_MATRIX_HTML "?col=%d\" BORDER=0 BGCOLOR=white><IMG class=tooltip SRC=\"" RRD_THPT_URL "\" border=\"0\" alt=\"Domain-wide Historical Data\"></A></td><td align=left><A HREF=\"" RRD_THPT_URL "&mode=zoom\" BORDER=0 BGCOLOR=white>&nbsp;<IMG valign=middle class=tooltip SRC=/graph_zoom.gif border=0></A></td></tr>"

void printThptStats(int sortedColumn _UNUSED_) {
  char tmpBuf[1024], formatBuf[32], formatBuf1[32];
  struct stat statbuf;
  int i, useRRD = 1;
  time_t now = time(NULL);

  printHTMLheader("Network Load Statistics", NULL, 0);

  if(strcmp(myGlobals.device[0].name, "pcap-file")) {
    /*
      if(myGlobals.device[myGlobals.actualReportDeviceId].dummyDevice) {
      printFlagedWarning("<I>Network load statistics are not available for virtual interfaces</I>");
      return;
      }
    */

    /* Do NOT add a '/' at the end of the path because Win32 will complain about it */
#ifdef WIN32
	  {
		  unsigned long driveSerial;

		  get_serial(&driveSerial);

		  safe_snprintf(__FILE__, __LINE__, tmpBuf, sizeof(tmpBuf), "%s/%u/interfaces/%s/throughput.rrd",
			  myGlobals.rrdPath != NULL ? myGlobals.spoolPath : ".", driveSerial,
			  myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName);
	  }
#else
    safe_snprintf(__FILE__, __LINE__, tmpBuf, sizeof(tmpBuf), "%s/interfaces/%s/throughput.rrd",
		  myGlobals.rrdPath != NULL ? myGlobals.spoolPath : ".",
		  myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName);
#endif

    revertSlashIfWIN32(tmpBuf, 0);

    if((i = stat(tmpBuf, &statbuf)) != 0) {
      useRRD = 0;
      sendString("<p align=left><b>NOTE</b>: this page is not operational when the <A HREF=/plugins/rrdPlugin>RRD plugin</A> "
		 "is disabled, misconfigured or missing. Please check the ntop log file.</p>");

      return;
    }

    if(useRRD) {
      sendString("<CENTER>\n<table border=0>\n");

      safe_snprintf(__FILE__, __LINE__, tmpBuf, sizeof(tmpBuf), RRD_THPT_STR,
		    0,
		    myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, (unsigned int)(now-600), 
		    (unsigned int)now, "Last+10+Minutes+Throughput",
		    myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, (unsigned int)(now-600), 
		    (unsigned int)now, "Throughput"
		    );
      sendString(tmpBuf);

      safe_snprintf(__FILE__, __LINE__, tmpBuf, sizeof(tmpBuf), "<tr><td align=center colspan=2><H4>Time [ %s through %s]</H4></td></tr>",
		    formatTimeStamp(0, 0, 10, formatBuf, sizeof(formatBuf)),
		    formatTimeStamp(0, 0,  0, formatBuf1, sizeof(formatBuf1)));
      sendString(tmpBuf);
    
      safe_snprintf(__FILE__, __LINE__, tmpBuf, sizeof(tmpBuf), RRD_THPT_STR,
		    1,
		    myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, (unsigned int)(now-3600), 
		    (unsigned int)now, "Last+Hour+Throughput",
		    myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, (unsigned int)(now-3600),
		    (unsigned int)now, "Throughput");
      sendString(tmpBuf);
    } 

    safe_snprintf(__FILE__, __LINE__, tmpBuf, sizeof(tmpBuf), "<tr><td align=center colspan=2><H4>Time [ %s through %s]</H4></td></tr>",
		  formatTimeStamp(0, 0, 60, formatBuf, sizeof(formatBuf)),
		  formatTimeStamp(0, 0,  0, formatBuf1, sizeof(formatBuf1)));
    sendString(tmpBuf);

    if(useRRD) {
      safe_snprintf(__FILE__, __LINE__, tmpBuf, sizeof(tmpBuf), RRD_THPT_STR,
		    2, myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, (unsigned int)(now-86400), 
		    (unsigned int)now, "Current+Day+Throughput",
		    myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, (unsigned int)(now-86400), 
		    (unsigned int)now, "Throughput");
      sendString(tmpBuf);
    }

    safe_snprintf(__FILE__, __LINE__, tmpBuf, sizeof(tmpBuf), "<tr><td align=center colspan=2><H4>Time [ %s through %s]</H4></td></tr>",
		  formatTimeStamp(0, 24, 0, formatBuf, sizeof(formatBuf)),
		  formatTimeStamp(0,  0, 0, formatBuf1, sizeof(formatBuf1)));
    sendString(tmpBuf);

    if(useRRD) {
      safe_snprintf(__FILE__, __LINE__, tmpBuf, sizeof(tmpBuf), RRD_THPT_STR,
		    3, myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, 
		    (unsigned int)(now-86400*30), (unsigned int)now, "Last+Month+Throughput",
		    myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, 
		    (unsigned int)(now-86400*30), (unsigned int)now, "Throughput");
      sendString(tmpBuf);
    }

    safe_snprintf(__FILE__, __LINE__, tmpBuf, sizeof(tmpBuf), "<tr><td align=center colspan=2><H4>Time [ %s through %s]</H4></td></tr>",
		  formatTimeStamp(30, 0, 0, formatBuf, sizeof(formatBuf)),
		  formatTimeStamp( 0, 0, 0, formatBuf1, sizeof(formatBuf1)));
    sendString(tmpBuf);

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

    if(useRRD) {
      safe_snprintf(__FILE__, __LINE__, tmpBuf, sizeof(tmpBuf), "<p align=right>"
		    "[ <A HREF=\"/" CONST_PLUGINS_HEADER"rrdPlugin\">Change Throughput Granularity</A> ]</p>",
		    formatTimeStamp(0, 0, 10, formatBuf, sizeof(formatBuf)),
		    formatTimeStamp(0, 0,  0, formatBuf1, sizeof(formatBuf1)));
      sendString(tmpBuf);
    } 
  }

  sendString("<p align=left><b>NOTE</b>: this page is not operational when the <A HREF=/plugins/rrdPlugin>RRD plugin</A> is disabled, misconfigured or missing.</p>");
}

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

static int cmpStatsFctn(const void *_a, const void *_b) {
  DomainStats *a = (DomainStats *)_a;
  DomainStats *b = (DomainStats *)_b;
  Counter a_, b_;
  int rc;

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

  /*
    traceEvent(CONST_TRACE_INFO, "--> [columnSort=%d][network_mode_sort=%d]",
    myGlobals.columnSort, network_mode_sort); 
  */

  switch(myGlobals.columnSort) {
  case 0:
    if(network_mode_sort == NETWORK_VIEW) {
      char buf1[64], buf2[64];

      char *nw_name_a = host2networkName(a->domainHost, buf1, sizeof(buf1));
      char *nw_name_b = host2networkName(b->domainHost, buf2, sizeof(buf2));
      rc = strcmp(nw_name_a, nw_name_b);

    } else if(network_mode_sort == AS_VIEW) {
      a_ = a->domainHost->hostAS , b_ = b->domainHost->hostAS;
    } else {
       /* Host */
      return(cmpFctnResolvedName(&(a->domainHost), &(b->domainHost)));
    }
    break;
  case 1: /* Domain Flag */
    /* We don't worry about whether this is single or multi domain, since if it is a single
       domain, our fallback to hostResolvedName will rule anyway.
    */
    if(a->clusterName && b->clusterName)
	rc = strcmp(a->clusterName, b->clusterName);
      else
	rc = cmpFctnLocationName(a, b);
      return(rc);
  case 2: a_  = a->bytesSent.value, b_ = b->bytesSent.value;   break;
  case 3: a_  = a->bytesRcvd.value, b_ = b->bytesRcvd.value;   break;
  case 4: a_  = a->tcpSent.value  , b_ = b->tcpSent.value;     break;
  case 5: a_  = a->tcpRcvd.value  , b_ = b->tcpRcvd.value;     break;
  case 6: a_  = a->udpSent.value  , b_ = b->udpSent.value;     break;
  case 7: a_  = a->udpRcvd.value  , b_ = b->udpRcvd.value;     break;
  case 8: a_  = a->icmpSent.value , b_ = b->icmpSent.value;    break;
  case 9: a_  = a->icmpRcvd.value , b_ = b->icmpRcvd.value;    break;
  case 10:a_  = a->icmp6Sent.value , b_ = b->icmp6Sent.value;  break;
  case 11:a_  = a->icmp6Rcvd.value , b_ = b->icmp6Rcvd.value;  break;
  }

  if(a_ < b_)
    return(1);
  else if(a_ > b_)
    return(-1);
  else
    return(0);
}

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

#define CLUSTER_HEADER       "cluster."
#define CLUSTER_HEADER_LEN   strlen(CLUSTER_HEADER)
#define MAX_NUM_CLUSTERS     16

/* if myGlobals.runningPref.domainName == NULL -> print all domains */
void printDomainStats(char* domain_network_name, int network_mode,
		      int clusterMode, int sortedColumn, 
		      int revertOrder, int pageNum) {
  u_int idx, tmpIdx, numEntries=0, printedEntries=0, maxHosts;
  u_short keyValue=0, i;
  HostTraffic *el;
  char buf[LEN_GENERAL_WORK_BUFFER], buf1[64];
  DomainStats **stats, *tmpStats = NULL, *statsEntry;
  char htmlAnchor[2*LEN_GENERAL_WORK_BUFFER], htmlAnchor1[2*LEN_GENERAL_WORK_BUFFER],
    *sign, *arrowGif, *arrow[48], *theAnchor[48];
  Counter totBytesSent=0, totBytesRcvd=0;
  char formatBuf[32], formatBuf1[32], formatBuf2[32], formatBuf3[32], formatBuf4[32],
    formatBuf5[32], formatBuf6[32], formatBuf7[32], formatBuf8[32], formatBuf9[32],
    hostLinkBuf[LEN_GENERAL_WORK_BUFFER];
  u_int32_t localNetworks[MAX_NUM_CLUSTERS][MAX_NUM_NETWORKS][4]; /* [0]=network, [1]=mask, [2]=broadcast, [3]=mask_v6 */
  u_short numLocalNetworks[MAX_NUM_CLUSTERS], totNumClusters=0;
  u_char *clusterNames[MAX_NUM_CLUSTERS], debug = 0;

  network_mode_sort = network_mode;

  if(!clusterMode) {
    char sym_nw_name[256] = { 0 };

    if(domain_network_name == NULL)
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Statistics for all %s",
		    (network_mode == NETWORK_VIEW) ? "Networks" : ((network_mode == AS_VIEW) ? "ASs" : "Domains"));
    else {
      char link_name[256] = { 0 };

      if(network_mode > 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s.name.%s",
		      (network_mode == NETWORK_VIEW) ? "network" : "as",
		      domain_network_name);
	
	if(fetchPrefsValue(buf, sym_nw_name, sizeof(sym_nw_name)) == -1)
	  sym_nw_name[0] = '\0';
       
	safe_snprintf(__FILE__, __LINE__, link_name, sizeof(link_name),
		      " <A HREF=\"%s?key=%s.name.%s\">%s</A>",
		      CONST_EDIT_PREFS, 
		      (network_mode == NETWORK_VIEW) ? "network" : "as",
		      domain_network_name,
		      "<img class=tooltip alt=\"Change name\" "
		      "src=/"CONST_EDIT_IMG" border=\"0\">");
      }

      if(network_mode == AS_VIEW) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), 
		      "Statistics for hosts in AS <A HREF=http://ws.arin.net/cgi-bin/whois.pl?queryinput=AS%s>%s</A> %s",
		      domain_network_name, (sym_nw_name[0] == '\0') ? domain_network_name : sym_nw_name,
		      link_name);
      } else if(network_mode == AS_GRAPH_VIEW) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), 
		      "Statistics for AS <A HREF=http://ws.arin.net/cgi-bin/whois.pl?queryinput=AS%s>%s</A> %s",
		      domain_network_name, (sym_nw_name[0] == '\0') ? domain_network_name : sym_nw_name,
		      link_name);
      } else {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), 
		      "Statistics for hosts in %s %s %s",
		      (network_mode == NETWORK_VIEW) ? "network" : ((network_mode == AS_VIEW) ? "AS" : "domain"),
		      (sym_nw_name[0] == '\0') ? domain_network_name : sym_nw_name,
		      link_name);
      }
    }
  } else {
    char localAddresses[1024];
    datum key, nextkey;

    if(domain_network_name == NULL) {
      key = gdbm_firstkey(myGlobals.prefsFile);
      while (key.dptr) {
	char val[256];

	if((fetchPrefsValue(key.dptr, val, sizeof(val)) == 0)
	   && (!strncmp(key.dptr, CLUSTER_HEADER, CLUSTER_HEADER_LEN))) {
	  localAddresses[0] = '\0';
	  numLocalNetworks[totNumClusters] = 0;
	  handleAddressLists(val, localNetworks[totNumClusters], &numLocalNetworks[totNumClusters],
			     localAddresses, sizeof(localAddresses),
			     CONST_HANDLEADDRESSLISTS_CLUSTERS);
	  clusterNames[totNumClusters] = (u_char*)strdup((char*)&key.dptr[CLUSTER_HEADER_LEN]);
	  totNumClusters++;
	}

	nextkey = gdbm_nextkey (myGlobals.prefsFile, key);
	free (key.dptr);
	key = nextkey;
      }

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Statistics for all clusters");
    } else {
      char clusterAddresses[256];

      snprintf(buf, sizeof(buf), CLUSTER_HEADER"%s", domain_network_name);
      if(fetchPrefsValue(buf, clusterAddresses, sizeof(clusterAddresses)) != -1) {
	localAddresses[0] = '\0';
	numLocalNetworks[totNumClusters] = 0;
	handleAddressLists(clusterAddresses, localNetworks[totNumClusters], &numLocalNetworks[totNumClusters],
			   localAddresses, sizeof(localAddresses),
			   CONST_HANDLEADDRESSLISTS_CLUSTERS);
	/* clusterNames[totNumClusters] = (u_char*)strdup((char*)&domain_network_name[CLUSTER_HEADER_LEN]);  */
	clusterNames[totNumClusters] = (u_char*)strdup(domain_network_name);

	if(debug) traceEvent(CONST_TRACE_WARNING, "clusterNames[%d]=[%s]", totNumClusters,  clusterNames[totNumClusters]);

	totNumClusters++;
      }

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Statistics for hosts in cluster <i>%s</i>",
		    domain_network_name);
    }

    if(totNumClusters == 0) {
      printHTMLheader(buf, NULL, 0);
      printFlagedWarning("<I>Empty cluster list. Jump to <A HREF=/"CONST_EDIT_PREFS">cluster definition</A>.</I>");
      if(tmpStats != NULL) free(tmpStats);
      goto free_clusters;
    }
  }

  if(debug) traceEvent(CONST_TRACE_WARNING, "totNumClusters=%d", totNumClusters);
  
  printHTMLheader(buf, NULL, 0);

  maxHosts = myGlobals.device[myGlobals.actualReportDeviceId].hostsno; /* save it as it can change */
  tmpStats = (DomainStats*)mallocAndInitWithReportWarn(maxHosts*sizeof(DomainStats), "printDomainStats");
  if(tmpStats == NULL)
    return;

  /* Fix below courtesy of Francis Pintos <francis@arhl.com.hk> */
  stats = (DomainStats**)mallocAndInitWithReportWarn(maxHosts*sizeof(DomainStats*), "printDomainStats(2)");
  if(stats == NULL) {
    /* also free the block of memory allocated a few lines up */
    if(tmpStats != NULL) free(tmpStats);
    goto free_clusters;
    return;
  }

  if(network_mode == AS_GRAPH_VIEW) {
    sendString("<center><TABLE BORDER=0 "TABLE_DEFAULTS">");
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<tr><td align=center>"
		  "<IMG SRC=/plugins/rrdPlugin?action=interfaceSummary&amp;key=%s/AS/%s&amp;graphId=0\">\n",
		  myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, domain_network_name);
    sendString(buf);
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "</td><td valign=middle>"
		  "<A HREF=/plugins/rrdPlugin?mode=zoom&action=interfaceSummary&amp;key=%s/AS/%s&amp;graphId=0\">"
		  "<IMG valign=middle class=tooltip SRC=/graph_zoom.gif border=0></A></td></tr>\n",
		  myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, domain_network_name);
    sendString(buf);

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<tr><td align=center>"
		  "<IMG SRC=/plugins/rrdPlugin?action=interfaceSummary&amp;key=%s/AS/%s&amp;graphId=1\">\n",
		  myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, domain_network_name);
    sendString(buf);
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "</td><td valign=middle>"
		  "<A HREF=/plugins/rrdPlugin?mode=zoom&action=interfaceSummary&amp;key=%s/AS/%s&amp;graphId=1\">"
		  "<IMG valign=middle class=tooltip SRC=/graph_zoom.gif border=0></A></td></tr>\n",
		  myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, domain_network_name);
    sendString(buf);
    sendString("</table>\n</center>\n");
  } else {
    /* traceEvent(CONST_TRACE_INFO, "'%s' '%d' '%d'", domain_network_name, sortedColumn, revertOrder); */

    if((network_mode == AS_VIEW) && (domain_network_name == NULL)) {
      struct stat statbuf;
      
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s/interfaces/%s/AS/numAS.rrd", 
		    myGlobals.rrdPath, myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName);
      
      if((i = stat(buf, &statbuf)) == 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<center>"
		      "<IMG SRC=\"/plugins/rrdPlugin?action=arbreq&which=graph&arbfile=numAS&arbiface=%s&start=%u&end=%u&counter=&title=%s\">",
		      myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, (unsigned int)(myGlobals.actTime-3600), 
		      (unsigned int)myGlobals.actTime, "Active+ASs");
	sendString(buf);

	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), 
		      "<A HREF=\"/plugins/rrdPlugin?action=arbreq&which=graph&arbfile=numAS&arbiface=%s&start=%u&end=%u&counter=&title=%s&mode=zoom\">"
		      "&nbsp;<IMG valign=middle class=tooltip SRC=/graph_zoom.gif border=0></A>",
		      myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, (unsigned int)(myGlobals.actTime-3600), 
		      (unsigned int)myGlobals.actTime, "Active+ASs");
	sendString(buf);


	sendString("</center><p>&nbsp;<p>");
      }
    }

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

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

      if(network_mode) {
	if(network_mode == NETWORK_VIEW) {
	  if((el->network_mask == 0) || (el->network_mask == 32))
	    continue;
	  else if((domain_network_name != NULL) && (domain_network_name[0] != '\0')) {
	    char *nw_name = host2networkName(el, buf1, sizeof(buf1));
	    if(strcmp(nw_name, domain_network_name))
	      continue;
	  }
	} else {
	  getHostAS(el);
	  if(el->hostAS == 0) 
	    continue;
	  else if((domain_network_name != NULL) && (domain_network_name[0] != '\0')) {
	    if(el->hostAS != atoi(domain_network_name))
	      continue;
	  }
	}
      } else {
	/* Cluster */
	if(el->community && (!isAllowedCommunity(el->community)))
	  continue;
	else if(broadcastHost(el))
	  continue;
	else {
	  /*
	  if(domain_network_name
	     && el->dnsDomainValue
	     && (strcmp(el->dnsDomainValue, domain_network_name) != 0))
	    continue;

	  if((el->dnsDomainValue == NULL)
	     || (el->dnsDomainValue[0] == '\0')
	     || (el->ip2ccValue == NULL)
	     || (el->hostResolvedName[0] == '\0')
	     || (el->ip2ccValue == '\0'))
	    continue;
	  */
	}
      }

      if(domain_network_name == NULL) /* All entries */ {
	if(!clusterMode) {
	  if(network_mode == NETWORK_VIEW) {
	    /* Network */
	    char *nw_name = host2networkName(el, buf1, sizeof(buf1));

	    for(keyValue=0, tmpIdx=0; nw_name[tmpIdx] != '\0'; tmpIdx++)
	      keyValue += (tmpIdx+1)*(u_short)nw_name[tmpIdx];

	    keyValue %= maxHosts;

	    while(stats[keyValue] != NULL) {
	      u_int32_t a, b;

	      a = el->hostIpAddress.Ip4Address.s_addr && (0xFFFFFFFF << (32-el->network_mask));
	      b = stats[keyValue]->domainHost->hostIpAddress.Ip4Address.s_addr
		&& (0xFFFFFFFF << (32-stats[keyValue]->domainHost->network_mask));

	      if(a == b)
		break;
	      else
		keyValue = (keyValue+1) % maxHosts;
	    }
	  } else if(network_mode == AS_VIEW) {
	    keyValue = (el->hostAS % maxHosts);

	    while(stats[keyValue] != NULL) {
	      if(stats[keyValue]->domainHost->hostAS == el->hostAS)
		break;
	      else
		keyValue = (keyValue+1) % maxHosts;
	    }

	    /* traceEvent(CONST_TRACE_INFO, "--> [AS=%d]", el->hostAS); */
	  } else {
		  if(el->dnsDomainValue == NULL) continue;

	    /* Domain */
	    for(keyValue=0, tmpIdx=0; el->dnsDomainValue[tmpIdx] != '\0'; tmpIdx++)
	      keyValue += (tmpIdx+1)*(u_short)el->dnsDomainValue[tmpIdx];

	    keyValue %= maxHosts;

	    while((stats[keyValue] != NULL)
		  && (strcasecmp(stats[keyValue]->domainHost->dnsDomainValue, el->dnsDomainValue) != 0))
	      keyValue = (keyValue+1) % maxHosts;
	  }
	} else {
	  /* Cluster */
	  u_char found = 0;

	  if(el->hostIpAddress.hostFamily != AF_INET) continue;

	  keyValue = 0;
	all_hosts_cluster:

	  if(debug) traceEvent(CONST_TRACE_WARNING, "[keyValue=%d][totNumClusters=%d]",
			       keyValue, totNumClusters);

	  for(; keyValue<totNumClusters; keyValue++) {
	    if(__pseudoLocalAddress(&el->hostIpAddress.addr._hostIp4Address,
				    localNetworks[keyValue], numLocalNetworks[keyValue], NULL, NULL)) {
	      found = 1;
	      break;
	    }
	  }

	  if((!found) || (keyValue >= totNumClusters  /* due to the goto */)) continue;
	}

	if(stats[keyValue] != NULL)
	  statsEntry = stats[keyValue];
	else {
	  statsEntry = &tmpStats[numEntries++];
	  memset(statsEntry, 0, sizeof(DomainStats));

	  if(clusterMode)
	    statsEntry->clusterName = (char*)clusterNames[keyValue];
	  else
	    statsEntry->domainHost = el;

	  stats[keyValue] = statsEntry;
	  /* traceEvent(CONST_TRACE_INFO, "[%d] %s/%s", numEntries, el->dnsDomainValue, el->ip2ccValue); */
	}
      } else /* Only the selected items */ {
	if(clusterMode) {
	  if(!__pseudoLocalAddress(&el->hostIpAddress.addr._hostIp4Address,
				   localNetworks[0], numLocalNetworks[0], NULL, NULL))
	    continue;
	}

	statsEntry = &tmpStats[numEntries++];
	memset(statsEntry, 0, sizeof(DomainStats));
	statsEntry->domainHost = el;
	if(debug) traceEvent(CONST_TRACE_INFO, "--> Adding %s [ptr=%p]", el->hostNumIpAddress, el);
	stats[keyValue++] = statsEntry;
      }

      totBytesSent                += el->bytesSent.value;
      statsEntry->bytesSent.value += el->bytesSent.value;
      statsEntry->bytesRcvd.value += el->bytesRcvd.value;
      totBytesRcvd                += el->bytesRcvd.value;
      statsEntry->tcpSent.value   += el->tcpSentLoc.value + el->tcpSentRem.value;
      statsEntry->udpSent.value   += el->udpSentLoc.value + el->udpSentRem.value;
      statsEntry->icmpSent.value  += el->icmpSent.value;
      statsEntry->icmp6Sent.value += el->icmp6Sent.value;
      statsEntry->tcpRcvd.value   += el->tcpRcvdLoc.value + el->tcpRcvdFromRem.value;
      statsEntry->udpRcvd.value   += el->udpRcvdLoc.value + el->udpRcvdFromRem.value;
      statsEntry->icmpRcvd.value  += el->icmpRcvd.value;
      statsEntry->icmp6Rcvd.value += el->icmp6Rcvd.value;

      /* Handle overlapping clusters */
      if(keyValue < (totNumClusters-1)) {
	keyValue++;
	goto all_hosts_cluster;
      }

      if(numEntries >= maxHosts) break;
    } /* for(;;) */

    if(numEntries == 0) {
      printNoDataYet();
      free(tmpStats); free(stats);
      goto free_clusters;
      return;
    }

    myGlobals.columnSort = sortedColumn;

    if(debug) traceEvent(CONST_TRACE_WARNING, "qsort(numEntries=%d)", numEntries);

    qsort(tmpStats, numEntries, sizeof(DomainStats), cmpStatsFctn);

    /* avoid division by zero */
    if(totBytesSent == 0)
      totBytesSent = 1;
    if(totBytesRcvd == 0)
      totBytesRcvd = 1;
  
    /* NOTE: col= must be the last parameter */
    if(domain_network_name == NULL) {
      safe_snprintf(__FILE__, __LINE__, htmlAnchor, sizeof(htmlAnchor), "<A HREF=/%s?netmode=%d&col=%s",
		    clusterMode ? CONST_CLUSTER_STATS_HTML : CONST_DOMAIN_STATS_HTML, network_mode, sign);
      safe_snprintf(__FILE__, __LINE__, htmlAnchor1, sizeof(htmlAnchor1), "<A HREF=/%s?netmode=%d&col=",
		    clusterMode ? CONST_CLUSTER_STATS_HTML : CONST_DOMAIN_STATS_HTML, network_mode);
    } else {
      safe_snprintf(__FILE__, __LINE__, htmlAnchor, sizeof(htmlAnchor), "<A HREF=/%s?dom=%s&netmode=%dcol=%s&",
		    clusterMode ? CONST_CLUSTER_STATS_HTML : CONST_DOMAIN_STATS_HTML, domain_network_name, network_mode, sign);
      safe_snprintf(__FILE__, __LINE__, htmlAnchor1, sizeof(htmlAnchor1), "<A HREF=/%s?dom=%s&netmode=%d&col=",
		    clusterMode ? CONST_CLUSTER_STATS_HTML : CONST_DOMAIN_STATS_HTML, domain_network_name, network_mode);
    }

    for(i=0; i<=15; i++)
      if(abs(myGlobals.columnSort) == i)
	arrow[i] = arrowGif, theAnchor[i] = htmlAnchor;
      else
	arrow[i] = "", theAnchor[i] = htmlAnchor1;

    /* Split below courtesy of Andreas Pfaller <apfaller@yahoo.com.au> */
    sendString("<CENTER>\n" TABLE_ON "<TABLE BORDER=1 "TABLE_DEFAULTS">");
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG" rowspan=\"3\">%s0>Name%s</A></TH>"
		  "<TH "TH_BG" rowspan=\"3\">%s1>%s%s</A></TH>"
		  "<TH "TH_BG" colspan=\"8\">TCP/IP</A></TH>"
		  "<TH "TH_BG" colspan=\"4\">ICMP</A></TH>"
		  "<TH "TH_BG">&nbsp;</TH></TR>\n",
		  theAnchor[0], arrow[0],
		  theAnchor[1], "Domains",		
		  arrow[1]);
    sendString(buf);

    sendString( "<TR "TR_ON" "DARK_BG">"
		"<TH "TH_BG" colspan=\"4\">Total</A></TH>"
		"<TH "TH_BG" colspan=\"2\">TCP</A></TH>"
		"<TH "TH_BG" colspan=\"2\">UDP</A></TH>"
		"<TH "TH_BG" colspan=\"2\">IPv4</A></TH>"
		"<TH "TH_BG" colspan=\"2\">IPv6</A></TH>"
		"<TH "TH_BG">Graphs</TH>\n"
		"</TR>\n");

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG" colspan=\"2\">%s2>Sent%s</A></TH>"
		  "<TH "TH_BG" colspan=\"2\">%s3>Rcvd%s</A></TH>"
		  "<TH "TH_BG">%s4>Sent%s</A></TH>"
		  "<TH "TH_BG">%s5>Rcvd%s</A></TH>"
		  "<TH "TH_BG">%s6>Sent%s</A></TH>"
		  "<TH "TH_BG">%s7>Rcvd%s</A></TH>"
		  "<TH "TH_BG">%s8>Sent%s</A></TH>"
		  "<TH "TH_BG">%s9>Rcvd%s</A></TH>"
		  "<TH "TH_BG">%s10>Sent%s</A></TH>"
		  "<TH "TH_BG">%s11>Rcvd%s</A></TH>"
		  "<TH "TH_BG">&nbsp;</TH>"
		  "</TR>\n",
		  theAnchor[2], arrow[2],
		  theAnchor[3], arrow[3],
		  theAnchor[4], arrow[4],
		  theAnchor[5], arrow[5],
		  theAnchor[6], arrow[6],
		  theAnchor[7], arrow[7],
		  theAnchor[8], arrow[8],
		  theAnchor[9], arrow[9],
		  theAnchor[10], arrow[10],
		  theAnchor[11], arrow[11]);
    sendString(buf);

    for(idx=pageNum*myGlobals.runningPref.maxNumLines; idx<numEntries; idx++) {
      char sym_as_name[256];
      struct stat statbuf;

      if(revertOrder)
	statsEntry = &tmpStats[numEntries-idx-1];
      else
	statsEntry = &tmpStats[idx];

      if(domain_network_name == NULL) {
	if(network_mode == NETWORK_VIEW) {
	  char *nw_name = host2networkName(statsEntry->domainHost, buf1, sizeof(buf1));
	  char sym_nw_name[256];

	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "network.name.%s",
			nw_name);

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

	  safe_snprintf(__FILE__, __LINE__, htmlAnchor, sizeof(htmlAnchor),
			"<A HREF=/%s?dom=%s&netmode=%d>%s</A>",
			CONST_DOMAIN_STATS_HTML, nw_name, network_mode,
			(sym_nw_name[0] == '\0') ? nw_name : sym_nw_name);
	
	} else if(network_mode == AS_VIEW) {
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "as.name.%d", 
			statsEntry->domainHost->hostAS);

	  if(fetchPrefsValue(buf, sym_as_name, sizeof(sym_as_name)) == -1)
	    snprintf(sym_as_name, sizeof(sym_as_name), "%d", statsEntry->domainHost->hostAS);

	  safe_snprintf(__FILE__, __LINE__, htmlAnchor, sizeof(htmlAnchor),
			"<A HREF=/%s?dom=%d&netmode=%d>%s</A>",
			CONST_DOMAIN_STATS_HTML, statsEntry->domainHost->hostAS, network_mode,
			sym_as_name);
	} else
	  safe_snprintf(__FILE__, __LINE__, htmlAnchor, sizeof(htmlAnchor), "<A HREF=/%s?dom=%s>%s</A>",
			clusterMode ? CONST_CLUSTER_STATS_HTML : CONST_DOMAIN_STATS_HTML,
			clusterMode ? statsEntry->clusterName : statsEntry->domainHost->dnsDomainValue,
			clusterMode ? statsEntry->clusterName : statsEntry->domainHost->dnsDomainValue);
      } else {
	char tmpBuf[64], *hostLink;
	int blankId;
	u_int len;

	accessAddrResMutex("getHostIcon");

	blankId = strlen(statsEntry->domainHost->hostResolvedName)-1;
	if(statsEntry->domainHost->dnsDomainValue != NULL)
	  blankId -= strlen(statsEntry->domainHost->dnsDomainValue);

	strncpy(tmpBuf, statsEntry->domainHost->hostResolvedName, sizeof(tmpBuf));

	releaseAddrResMutex();

	if((blankId > 0) && (strcmp(&tmpBuf[blankId+1], domain_network_name) == 0))
	  tmpBuf[blankId] = '\0';

	if(network_mode == AS_VIEW) {
	  makeHostLink(statsEntry->domainHost, FLAG_HOSTLINK_TEXT_FORMAT, 0,
		       0, htmlAnchor, sizeof(htmlAnchor));
	} else {
	  hostLink = makeHostLink(statsEntry->domainHost, FLAG_HOSTLINK_TEXT_FORMAT, 1,
				  0, hostLinkBuf, sizeof(hostLinkBuf));
	
	  len = strlen(hostLink); if(len >= sizeof(htmlAnchor)) len = sizeof(htmlAnchor)-1;
	  strncpy(htmlAnchor, hostLink, len);
	  htmlAnchor[len] = '\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=CENTER>%s</TD>"
		    "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%%</TD>"
		    "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%%</TD>",
		    getRowColor(), htmlAnchor,
		    (clusterMode && (!domain_network_name)) ? "&nbsp;" : getHostCountryIconURL(statsEntry->domainHost),
		    formatBytes(statsEntry->bytesSent.value, 1, formatBuf, sizeof(formatBuf)),
		    (100*((float)statsEntry->bytesSent.value/(float)totBytesSent)),
		    formatBytes(statsEntry->bytesRcvd.value, 1, formatBuf1, sizeof(formatBuf1)),
		    (100*((float)statsEntry->bytesRcvd.value/(float)totBytesRcvd)));
      sendString(buf);

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), 
		    "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		    "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		    "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
		    formatBytes(statsEntry->tcpSent.value, 1, formatBuf2, sizeof(formatBuf2)),
		    formatBytes(statsEntry->tcpRcvd.value, 1, formatBuf3, sizeof(formatBuf3)),
		    formatBytes(statsEntry->udpSent.value, 1, formatBuf4, sizeof(formatBuf4)),
		    formatBytes(statsEntry->udpRcvd.value, 1, formatBuf5, sizeof(formatBuf5)),
		    formatBytes(statsEntry->icmpSent.value, 1, formatBuf6, sizeof(formatBuf6)),
		    formatBytes(statsEntry->icmpRcvd.value, 1, formatBuf7, sizeof(formatBuf7)),
		    formatBytes(statsEntry->icmp6Sent.value, 1, formatBuf8, sizeof(formatBuf8)),
		    formatBytes(statsEntry->icmp6Rcvd.value, 1, formatBuf9, sizeof(formatBuf9)));
      sendString(buf);

      if(sym_as_name[0] != '\0')
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s/interfaces/%s/AS/%s", 
		      myGlobals.rrdPath, myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName, 
		      sym_as_name);
      
      if((sym_as_name[0] != '\0') && ((i = stat(buf, &statbuf)) == 0)) {
	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		       "<TD "TD_BG" ALIGN=CENTER><A HREF=\"/%s?netmode=%d&dom=%s\">"
		       "<img class=tooltip valign=top border=0 src=/graph.gif></A></TD></TR>\n",
		       CONST_DOMAIN_STATS_HTML, AS_GRAPH_VIEW, sym_as_name);
	 sendString(buf);
      } else if(clusterMode && statsEntry && statsEntry->clusterName) {
	 safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		       "<TD "TD_BG" ALIGN=CENTER><A HREF=\"/" CONST_PLUGINS_HEADER "rrdPlugin?action=list&cluster=%s\">"
		       "<img class=tooltip valign=top border=0 src=/graph.gif></A></TD></TR>\n",
		       statsEntry->clusterName);
	 sendString(buf);

      } else
	 sendString("<TD "TD_BG" ALIGN=CENTER>&nbsp;</TD>");

      /* Avoid huge tables */
      if(printedEntries++ > myGlobals.runningPref.maxNumLines)
	break;
    }

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

    if(domain_network_name != NULL) {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s?dom=%s",
		    clusterMode ? CONST_CLUSTER_STATS_HTML : CONST_DOMAIN_STATS_HTML, domain_network_name);
    } else {
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s",
		    clusterMode ? CONST_CLUSTER_STATS_HTML : CONST_DOMAIN_STATS_HTML);
    }

    addPageIndicator(buf, pageNum, numEntries,
		     myGlobals.runningPref.maxNumLines,
		     revertOrder, abs(sortedColumn), network_mode);
  

#ifndef EMBEDDED
    /* FIX */
    /* RRDs for domains */
    if(domain_network_name != NULL) {
      struct stat statbufDomain;

      /* Do NOT add a '/' at the end of the path because Win32 will complain about it */
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "%s/interfaces/%s/domains/%s",
		    myGlobals.rrdPath != NULL ? myGlobals.rrdPath : ".",
		    myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName,domain_network_name);

      revertSlashIfWIN32(buf, 0);

      if((i = stat(buf, &statbufDomain)) == 0) {
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		      "<p>&nbsp;</p>\n"
		      "<center><table border=\"0\"><tr>"
		      "<td valign=\"middle\" align=\"right\">Show domain-wide traffic charts:</td>\n"
		      "<td align=\"right\">"
		      "&nbsp;&nbsp;"
		      "[ <a href=\"/" CONST_PLUGINS_HEADER
		      "rrdPlugin?action=list&key=interfaces/%s/domains/%s&title=Domain%%20%s\">"
		      "<img border=\"0\" src=\"/graph.gif\" class=tooltip alt=\"Domain-wide Historical Data\"></a> ]"
		      "&nbsp;&nbsp;"
		      "</td>\n"
		      "</tr></table>\n</center>\n"
		      "<p>&nbsp;</p>\n",
		      myGlobals.device[myGlobals.actualReportDeviceId].uniqueIfName,
		      domain_network_name,domain_network_name);
	sendString(buf);
      }
    }
#endif

    if(!clusterMode) {
      sendString("<p align=\"center\"><b>NOTE</b>: ");
      if(network_mode == NETWORK_VIEW)
	sendString("<small>Hosts without a known network are not displayed in this report</small>\n");
      else if(network_mode == AS_VIEW)
	sendString("<small>AS numbers are either received via monitoring protocols (e.g. NetFlow) or read from the AS-list ntop configuration file</small>");
      else
	sendString("<small>The domain is determined by simply stripping off "
		   "the first name, so for host x.yz.com, the domain is yz.com and for host "
		   "x.y.z.com, the domain is y.z.com.</small></p>\n");
    } else {
      sendString("<p align=\"center\"><b>NOTE</b>: <small>You can define host clusters in the ntop <A HREF=/"CONST_EDIT_PREFS">preferences</A>. "
		 "Please understand that a host cluster is an aggregated view of hosts known to ntop.</small></p>\n");
    }

    free(tmpStats); free(stats);

  free_clusters:
    for(i=0; i<totNumClusters; i++)
      if(clusterNames[i] != NULL) free(clusterNames[i]);
  }
}

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

void printNoDataYet(void) {
  printFlagedWarning("<I>No Data To Display (yet)</I>");
}

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

void printNotAvailable(char * flagName) {
  char buf[LEN_GENERAL_WORK_BUFFER];
  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<I>The requested data is not available when ntop is"
		"<br>started with the command line flag %s</I>",
		flagName);
  printFlagedWarning(buf);
}

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

void listNetFlows(void) {
  char buf[LEN_GENERAL_WORK_BUFFER];
  int numEntries=0;
  FlowFilterList *list = myGlobals.flowsList;
  char formatBuf[32], formatBuf1[32];

  printHTMLheader(NULL, NULL, 0);

  if(list != NULL) {
    while(list != NULL) {
      if(list->pluginStatus.activePlugin) {
	if(numEntries == 0) {
	  printSectionTitle("Network Flows");
 	  sendString("<CENTER>\n");
	  sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS"><TR "TR_ON" "DARK_BG"><TH "TH_BG">Flow Name</TH>"
  		     "<TH "TH_BG">Packets</TH><TH "TH_BG">Traffic</TH></TR>");
  	}

	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",
		      getRowColor(), list->flowName,
		      formatPkts(list->packets.value, formatBuf, sizeof(formatBuf)),
		      formatBytes(list->bytes.value, 1, formatBuf1, sizeof(formatBuf1)));
	sendString(buf);

	numEntries++;
      }

      list = list->next;
    }

    if(numEntries > 0)
      sendString("</TABLE>"TABLE_OFF"\n");

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

  sendString("<p align=left><b>NOTE</b>: Network flows have <u>no relation at all</u> with NetFlow/sFlow protocols.</p>\n");

  if(numEntries == 0) {
    sendString("<CENTER><P><H1>No Available/Active Network Flows</H1><p>"
	       " (see <A HREF=" CONST_MAN_NTOP_HTML ">man</A> page)</CENTER>\n");
  }
}

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

void printHostHourlyTraffic(HostTraffic *el) {
  Counter tcSent, tcRcvd;
  int i, hourId, j;
  char theDate[8], macAddr[24], vlanStr[32];
  struct tm t;
  char buf[LEN_GENERAL_WORK_BUFFER], *targetStr;
  char hours[][24] = {"12 AM", "1 AM", "2 AM", "3 AM", "4 AM", "5 AM", "6 AM",
		      "7 AM", "8 AM", "9 AM", "10 AM", "11 AM", "12 PM", "1 PM",
		      "2 PM", "3 PM", "4 PM", "5 PM", "6 PM", "7 PM", "8 PM",
		      "9 PM", "10 PM", "11 PM"};

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

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

  /* In FC, where traffic is mostly storage traffic, the word "host" has a
   * specific meaning (SCSI Initiator) and so we distinguish the title for
   * FC_Ports
   */
  if(isFcHost (el)) {
    printSectionTitle("FibreChannel Port Traffic Stats");
  }
  else {
    printSectionTitle("Host Traffic Stats");
  }

  sendString("<CENTER>\n");
  sendString(""TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS" WIDTH=\"80%\">\n<TR "TR_ON" "DARK_BG">");
  sendString("<TH "TH_BG">Time</TH>");
  sendString("<TH "TH_BG">Tot. Traffic Sent</TH>");
  sendString("<TH "TH_BG">% Traffic Sent</TH>");
  sendString("<TH "TH_BG">Tot. Traffic Rcvd</TH>");
  sendString("<TH "TH_BG">% Traffic Rcvd</TH></TR>");

  for(i=0, tcSent=0, tcRcvd=0; i<24; i++) {
    tcSent += el->trafficDistribution->last24HoursBytesSent[i].value;
    tcRcvd += el->trafficDistribution->last24HoursBytesRcvd[i].value;
  }

  for (i = 0, j = hourId; i < 24; i++) {
    j = j%24;
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON"><TH "TH_BG" ALIGN=RIGHT "DARK_BG">%s</TH>\n", hours[j]);
    sendString(buf);
    printHostHourlyTrafficEntry(el, j, tcSent, tcRcvd);
    if(!j)
      j = 23;
    else
      j--;
  }

  sendString("<TR "TR_ON"><TH "TH_BG" "DARK_BG">Total</TH>\n");

  if(isFcHost (el)) {
    targetStr = el->fcCounters->hostNumFcAddress;
  }
  else {
    safe_snprintf(__FILE__, __LINE__, macAddr, sizeof(macAddr), "%s", el->ethAddressString);
    targetStr = el->hostNumIpAddress[0] == '\0' ?  macAddr : el->hostNumIpAddress;
  }

  urlFixupToRFC1945Inplace(targetStr);

  if(el->vlanId > 0) {
    safe_snprintf(__FILE__, __LINE__, vlanStr, sizeof(vlanStr), "-%d", el->vlanId);
  } else
    vlanStr[0] = '\0';

  if(tcSent > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		  "<TD ALIGN=CENTER COLSPAN=2 "TD_BG" BGCOLOR=white>"
		  "<iframe frameborder=0 SRC=\"/hostTimeTrafficDistribution-%s%s"CHART_FORMAT"?1\" width=400 height=250></iframe></TD>\n",
		  targetStr, vlanStr);
    sendString(buf);
  } else
    sendString("<TD COLSPAN=2 "TD_BG">&nbsp;</TD>\n");

  if(tcRcvd > 0) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD ALIGN=CENTER COLSPAN=2 "TD_BG" BGCOLOR=white>"
		  "<iframe frameborder=0 SRC=\"/hostTimeTrafficDistribution-%s%s"CHART_FORMAT"\" width=400 height=250></iframe>"
		  "</TD>\n",
		  targetStr, vlanStr);
    sendString(buf);
  } else
    sendString("<TD COLSPAN=2 "TD_BG">&nbsp;</TD>\n");

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

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

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

static void dumpHostsCriteria(NtopInterface *ifName, u_char criteria) {
  u_int numEntries=0, i, maxHosts;
  HostTraffic **tmpTable, *el;
  char buf[LEN_GENERAL_WORK_BUFFER];
  char formatBuf[32], formatBuf1[32], hostLinkBuf[LEN_GENERAL_WORK_BUFFER];

  maxHosts = ifName->hostsno; /* save it as it can change */

  tmpTable = (HostTraffic**)mallocAndInitWithReportWarn(maxHosts*sizeof(HostTraffic*), "dumpHostsCriteria");
  if(tmpTable == NULL)
    return;

  switch(criteria) {
  case 1: /* VLAN */
    myGlobals.columnSort = CONST_VLAN_COLUMN_SORT;
    break;
  }

  for(el=getFirstHost(myGlobals.actualReportDeviceId);
      el != NULL; el = getNextHost(myGlobals.actualReportDeviceId, el)) {
    if(el->community && (!isAllowedCommunity(el->community))) continue;
    switch(criteria) {
    case 1: /* VLAN */
      if(el->vlanId > 0)  tmpTable[numEntries++] = el;
      break;
    }

    if(numEntries >= maxHosts)
      break;
  }

  if(numEntries > 0) {
    int lastId = 0;
    Counter dataSent, dataRcvd;

    qsort(tmpTable, numEntries, sizeof(HostTraffic*), sortHostFctn);

    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<CENTER>"TABLE_ON"<TABLE BORDER=1 "TABLE_DEFAULTS">\n"
		  "<TR "TR_ON" "DARK_BG">"
		  "<TH "TH_BG">%s</A></TH>\n"
		  "<TH "TH_BG">Hosts</TH>\n"
		  "<TH "TH_BG">Data Sent</TH>\n"
		  "<TH "TH_BG">Data Rcvd</TH></TR>\n",
		  "VLAN");
    sendString(buf);

    dataSent = dataRcvd = 0;

    for(i=0; i<numEntries; i++) {
      el = tmpTable[numEntries-i-1];

      if((criteria == 1) && (lastId == el->vlanId)) {
        /* Same VLAN as last entry... just continue it */
        sendString("\n<br>");
      } else {
        /* New VLAN */

        if(i > 0) {
          /* Finish prior row */
	  safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
			"<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n"
			"<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n"
			"</TR>\n",
			formatBytes(dataSent, 1, formatBuf, sizeof(formatBuf)),
			formatBytes(dataRcvd, 1, formatBuf1, sizeof(formatBuf1)));
	  sendString(buf);
        }

        /* Start new row */
	dataSent = dataRcvd = 0;

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

	lastId = el->vlanId;
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf),
		      "<TH "TH_BG" ALIGN=RIGHT>%d</TH>\n",
		      el->vlanId);
        sendString(buf);

        sendString("<TH "TH_BG" ALIGN=LEFT>");

      }

      sendString(makeHostLink(el, FLAG_HOSTLINK_TEXT_FORMAT, 0, 0,
                              hostLinkBuf, sizeof(hostLinkBuf)));

      dataSent += el->bytesSent.value;
      dataRcvd += el->bytesRcvd.value;
    }

    if(i>0) {
      sendString("</TH>\n");

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n"
		    "<TD "TD_BG" ALIGN=RIGHT>%s</TD>\n",
		    formatBytes(dataSent, 1, formatBuf, sizeof(formatBuf)),
		    formatBytes(dataRcvd, 1, formatBuf1, sizeof(formatBuf1)));
      sendString(buf);
    }

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

  } else {
    printFlagedWarning("<I>No entries to display(yet)</I>");
  }

  free(tmpTable);
}

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

void printVLANList(unsigned int deviceId) {
  printHTMLheader("VLAN Traffic Statistics", NULL, 0);

  if(deviceId > myGlobals.numDevices) {
    printFlagedWarning("<I>Invalid device specified</I>");
    return;
  }

  if(myGlobals.haveVLANs == FALSE) {
    printFlagedWarning("<I>No VLANs found (yet)</I>");
    return;
  }

  dumpHostsCriteria(&myGlobals.device[deviceId], 1 /* VLAN */);
}

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

static int recentlyUsedPort(HostTraffic *el, int portNr, int serverPort) {
	int i;

	if(el == NULL) return(0);

	for(i=0; i<MAX_NUM_RECENT_PORTS; i++) {
		if(serverPort) {
			if(el->recentlyUsedServerPorts[i] == portNr)
				return(1);
		} else {
			if(el->recentlyUsedClientPorts[i] == portNr)
				return(1);
		}
	}

	return(0);
}

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

void showPortTraffic(u_short portNr) {
  char buf[LEN_GENERAL_WORK_BUFFER], *str;
  int numRecords = 0, firstRun = 1;
  HostTraffic *el;
  char portBuf[32], hostLinkBuf[LEN_GENERAL_WORK_BUFFER];

  str = getAllPortByNum(portNr, portBuf, sizeof(portBuf));

  if((str[0] == '?') || (atoi(str) == portNr)) {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Recent Users of Port %u", portNr);
  } else {
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "Recent Users of Port %u (%s)", portNr, str);
  }

  printHTMLheader(buf, NULL, 0);
  sendString("<CENTER>\n");

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

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

  recentlyUsedPortSent:
    if(recentlyUsedPort(el, portNr, 0)) {
      if(numRecords == 0) {
	sendString("<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR "DARK_BG"><TH>Client</TH><TH>Server</TH></TR>\n");
	sendString("<TR>\n<TD nowrap><ul>\n");
      }

      sendString("\n<LI> ");
      sendString(makeHostLink(el, FLAG_HOSTLINK_TEXT_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)));
      numRecords++;
    }

    if(el == myGlobals.broadcastEntry) break;
  }

  if(firstRun) {
    firstRun=0;
    el = myGlobals.broadcastEntry;
    goto recentlyUsedPortSent;
  }

  firstRun = 1;

  if(numRecords > 0) {
    sendString("\n&nbsp;\n</ul></TD><TD nowrap><ul>\n");
  }

  for(el=getFirstHost(myGlobals.actualReportDeviceId);
      el != NULL; el = getNextHost(myGlobals.actualReportDeviceId, el)) {
    if(el->community && (!isAllowedCommunity(el->community))) continue;

  recentlyUsedPortRcvd:
    if(recentlyUsedPort(el, portNr, 1)) {
      if(numRecords == 0) {
		sendString("<TABLE BORDER=1 "TABLE_DEFAULTS">\n<TR "DARK_BG"><TH>Client</TH><TH>Server</TH></TR>\n");
		sendString("<TR>\n<TD>\n");
		sendString("\n&nbsp;\n</TD><TD nowrap><ul>\n");
      }

      sendString("\n<LI> ");
      sendString(makeHostLink(el, FLAG_HOSTLINK_TEXT_FORMAT, 0, 0, hostLinkBuf, sizeof(hostLinkBuf)));
      numRecords++;
    }

    if(el == myGlobals.broadcastEntry) break;
  }


  if(firstRun) {
    firstRun = 0;
    el = myGlobals.broadcastEntry;
    goto recentlyUsedPortRcvd;
  }

  if(numRecords == 0) {
	  safe_snprintf(__FILE__, __LINE__, hostLinkBuf, sizeof(hostLinkBuf),
		  "<P>No hosts found: the information for this port "
		  "has been purged in the meantime <br>"
		  "as each host keeps the last %d server/client ports only.</CENTER><P>\n",
		  MAX_NUM_RECENT_PORTS);

		  sendString(hostLinkBuf);
  } else
    sendString("\n&nbsp;\n</ul></TD>\n</TR>\n</TABLE>\n</CENTER>");

}

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

void printFcHostsTraffic(int reportType,
                         int sortedColumn,
                         int revertOrder,
                         int pageNum,
                         char* url,
                         LocalityDisplayPolicy showLocalityMode) {
  u_int idx, numEntries=0, maxHosts, whatToDo;
  int printedEntries=0, hourId;
  char theDate[8];
  struct tm t;
  HostTraffic *el;
  HostTraffic** tmpTable;
  char buf[LEN_GENERAL_WORK_BUFFER], formatBuf[8][32], vsanBuf[LEN_MEDIUM_WORK_BUFFER];
  char hostLinkBuf[LEN_GENERAL_WORK_BUFFER];
  float sentPercent=0, rcvdPercent=0, totPercent=0;
  Counter totFcBytesSent=0, totFcBytesRcvd=0, totFcBytes=0;

  memset(buf, 0, sizeof(buf));
  switch(reportType) {
  case SORT_FC_ACTIVITY:
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "FibreChannel Activity");
    myGlobals.reportKind = SORT_DATA_HOST_TRAFFIC;
    showLocalityMode = 0;
    break;
  case SORT_FC_DATA:
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "FibreChannel Traffic: ");
    break;
  case SORT_FC_THPT:
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "FibreChannel Throughput: ");
    break;
  default:
    safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "?? %d : ", reportType);
    break;
  }

  if(reportType != SORT_FC_ACTIVITY) {
    switch(showLocalityMode) {
    case showSentReceived:
      strncat(buf, "Data Sent+Received", sizeof(buf) - strlen(buf) - 1);
      myGlobals.reportKind = (reportType == SORT_FC_DATA) ?
	SORT_DATA_PROTOS : SORT_DATA_THPT;
      break;
    case showOnlySent:
      strncat(buf, "Data Sent", sizeof(buf) - strlen(buf) - 1);
      myGlobals.reportKind = (reportType == SORT_FC_DATA) ?
	SORT_DATA_SENT_PROTOS : SORT_DATA_SENT_THPT;
      break;
    case showOnlyReceived:
      strncat(buf, "Data Received", sizeof(buf) - strlen(buf) - 1);
      myGlobals.reportKind = (reportType == SORT_FC_DATA) ?
	SORT_DATA_RECEIVED_PROTOS : SORT_DATA_RECEIVED_THPT;
      break;
    }
  }
  printHTMLheader(buf, 0, 0);

  maxHosts = myGlobals.device[myGlobals.actualReportDeviceId].hostsno; /* save it as it can change */

  tmpTable =  (HostTraffic**)mallocAndInitWithReportWarn(myGlobals.device[myGlobals.actualReportDeviceId].
							 actualHashSize*sizeof(HostTraffic*), "printFcHostsTraffic");
  if(tmpTable == NULL)
    return;

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

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

    if(el->community && (!isAllowedCommunity(el->community))) continue;
    if(isFcHost (el)) {
      /* Skip Control VSAN traffic */
      if(el->fcCounters->vsanId > MAX_USER_VSAN) continue;

      if(((showLocalityMode == showOnlySent) && (el->fcCounters->fcBytesSent.value > 0))
	  || ((showLocalityMode == showOnlyReceived) && (el->fcCounters->fcBytesRcvd.value > 0))
	  || ((showLocalityMode == showSentReceived) && ((el->fcCounters->fcBytesSent.value > 0) ||
							 (el->fcCounters->fcBytesRcvd.value > 0)))) {
	tmpTable[numEntries++]=el;

	if(numEntries >= maxHosts)
	  break;
      }
    }
  }

  if(numEntries <= 0) {
    printNoDataYet();
    free(tmpTable);
    return;
  }

  if(reportType != SORT_FC_ACTIVITY) {
    switch(showLocalityMode) {
    case showSentReceived:
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<p align=\"right\">"
		    "[<b> All </b>]&nbsp;"
		    "[ <a href=\"%s?col=%s%d&showL=1\">Sent Only</a> ]&nbsp;"
		    "[ <a href=\"%s?col=%s%d&showL=2\">Received Only</a> ]&nbsp;</p>",
		    url, revertOrder ? "-" : "", sortedColumn,
		    url, revertOrder ? "-" : "", sortedColumn);
      break;
    case showOnlySent:
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<p align=\"right\">"
		    "[ <a href=\"%s?col=%s%d&showH=%d&showL=0\">All</a> ]&nbsp;"
		    "[<b> Sent Only </b>]&nbsp;"
		    "[ <a href=\"%s?col=%s%d&showH=%d&showL=2\">Received Only</a> ]&nbsp;</p>",
		    url, revertOrder ? "-" : "", sortedColumn, showLocalityMode,
		    url, revertOrder ? "-" : "", sortedColumn, showLocalityMode);
      break;
    default:
      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<p align=\"right\">"
		    "[ <a href=\"%s?col=%s%d&showH=%d&showL=0\">All</a> ]&nbsp;"
		    "[ <a href=\"%s?col=%s%d&showH=%d&showL=1\">Sent Only</a> ]&nbsp;"
		    "[<b> Received Only </b>]&nbsp;</p>",
		    url, revertOrder ? "-" : "", sortedColumn, showLocalityMode,
		    url, revertOrder ? "-" : "", sortedColumn, showLocalityMode);
      break;
    }
    sendString(buf);
  }

  printFcHeader(reportType, revertOrder, abs(sortedColumn), hourId, url);

  myGlobals.columnSort = sortedColumn;

#ifdef FC_DEBUG
  traceEvent(CONST_TRACE_INFO, "FC_DEBUG: reportType=%d/sortedColumn=%d/myGlobals.columnSort=%d",
	     reportType, sortedColumn, myGlobals.columnSort);
#endif

  qsort(tmpTable, numEntries, sizeof(HostTraffic*), cmpFcFctn);

  totFcBytesSent = totFcBytesRcvd = totFcBytes = 0;

  for(idx=0; idx<numEntries; idx++) {
    if(tmpTable[idx] != NULL) {
      /* Count 'em both - not worth the IFs */
      totFcBytesSent += tmpTable[idx]->fcCounters->fcBytesSent.value;
      totFcBytesRcvd += tmpTable[idx]->fcCounters->fcBytesRcvd.value;
      switch(showLocalityMode) {
      case showSentReceived:
	totFcBytes += tmpTable[idx]->fcCounters->fcBytesSent.value +
	  tmpTable[idx]->fcCounters->fcBytesRcvd.value;
	break;
      case showOnlySent:
	totFcBytes += tmpTable[idx]->fcCounters->fcBytesSent.value;
	break;
      case showOnlyReceived:
	totFcBytes += tmpTable[idx]->fcCounters->fcBytesRcvd.value;
	break;
      }
    }
  }

  /* Avoid core dumps */
  if(totFcBytesSent == 0) totFcBytesSent = 1;
  if(totFcBytesRcvd == 0) totFcBytesRcvd = 1;
  if(totFcBytes == 0)     totFcBytes = 1;

#ifdef FC_DEBUG
  traceEvent(CONST_TRACE_INFO, "FC_DEBUG: totIpBytesSent=%u, totIpBytesRcvd=%u totIpBytes=%u",
	     totFcBytesSent, totFcBytesRcvd, totFcBytes);
#endif

  for(idx=pageNum*myGlobals.runningPref.maxNumLines; idx<numEntries; idx++) {
    char webHostName[LEN_GENERAL_WORK_BUFFER];

    if(revertOrder)
      el = tmpTable[numEntries-idx-1];
    else
      el = tmpTable[idx];

    if(el != NULL) {
      sentPercent = (100*(float)el->fcCounters->fcBytesSent.value)/totFcBytesSent;
      rcvdPercent = (100*(float)el->fcCounters->fcBytesRcvd.value)/totFcBytesRcvd;
      totPercent = (100*(float) (el->fcCounters->fcBytesSent.value + el->fcCounters->fcBytesRcvd.value) )/totFcBytes;

      strncpy(webHostName,
	      makeFcHostLink(el, FLAG_HOSTLINK_HTML_FORMAT, 0, 0,
			     hostLinkBuf, sizeof(hostLinkBuf)),
	      sizeof(webHostName));

      /* We have two reports x three data breakdowns + activity...
	 cheat and create a number for a 1d switch() 100*Locality + SORT_FC
	 precise formula is irrelevant, as long as it's unique...
      */
      whatToDo = 100*showLocalityMode + reportType;

      switch(whatToDo) {
      case (100*showSentReceived + SORT_FC_DATA):
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>"
                      "%s%s"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%s%%</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>",
		      getRowColor(),
                      makeVsanLink (el->fcCounters->vsanId, FLAG_HOSTLINK_HTML_FORMAT,
                                    vsanBuf, sizeof(vsanBuf)),
                      webHostName,
		      formatBytes(el->fcCounters->fcBytesSent.value     +el->fcCounters->fcBytesRcvd.value,      1, formatBuf[1], 32),
                      totPercent, myGlobals.separator,
                      formatBytes(el->fcCounters->fcFcpBytesSent.value  +el->fcCounters->fcFcpBytesRcvd.value,   1, formatBuf[2], 32),
		      formatBytes(el->fcCounters->fcElsBytesSent.value  +el->fcCounters->fcElsBytesRcvd.value,   1, formatBuf[3], 32),
		      formatBytes(el->fcCounters->fcDnsBytesSent.value  +el->fcCounters->fcDnsBytesRcvd.value,   1, formatBuf[4], 32),
                      formatBytes(el->fcCounters->fcIpfcBytesSent.value +el->fcCounters->fcIpfcBytesRcvd.value,  1, formatBuf[5], 32),
                      formatBytes(el->fcCounters->fcSwilsBytesSent.value+el->fcCounters->fcSwilsBytesRcvd.value, 1, formatBuf[6], 32),
                      formatBytes(el->fcCounters->otherFcBytesSent.value+el->fcCounters->otherFcBytesRcvd.value, 1, formatBuf[7], 32));
	break;
      case (100*showOnlySent + SORT_FC_DATA):
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>"
                      "%s%s"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%s%%</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>",
		      getRowColor(),
                      makeVsanLink (el->fcCounters->vsanId, FLAG_HOSTLINK_HTML_FORMAT,
                                    vsanBuf, sizeof(vsanBuf)),
                      webHostName,
		      formatBytes(el->fcCounters->fcBytesSent.value, 1, formatBuf[1], 32),
                      sentPercent, myGlobals.separator,
                      formatBytes(el->fcCounters->fcFcpBytesSent.value, 1, formatBuf[2], 32),
		      formatBytes(el->fcCounters->fcElsBytesSent.value, 1, formatBuf[3], 32),
		      formatBytes(el->fcCounters->fcDnsBytesSent.value, 1, formatBuf[4], 32),
                      formatBytes(el->fcCounters->fcIpfcBytesSent.value, 1, formatBuf[5], 32),
                      formatBytes(el->fcCounters->fcSwilsBytesSent.value, 1, formatBuf[6], 32),
                      formatBytes(el->fcCounters->otherFcBytesSent.value, 1, formatBuf[7], 32));
	break;
      case (100*showOnlyReceived + SORT_FC_DATA):
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>"
                      "%s%s"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%.1f%s%%</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD><TD "TD_BG" ALIGN=RIGHT>%s</TD>",
		      getRowColor(),
                      makeVsanLink (el->fcCounters->vsanId, FLAG_HOSTLINK_HTML_FORMAT, vsanBuf,
                                    sizeof(vsanBuf)),
                      webHostName,
                      formatBytes(el->fcCounters->fcBytesRcvd.value, 1, formatBuf[1], 32),
		      rcvdPercent, myGlobals.separator,
		      formatBytes(el->fcCounters->fcFcpBytesRcvd.value, 1, formatBuf[2], 32),
		      formatBytes(el->fcCounters->fcElsBytesRcvd.value, 1, formatBuf[3], 32),
		      formatBytes(el->fcCounters->fcDnsBytesRcvd.value, 1, formatBuf[4], 32),
                      formatBytes(el->fcCounters->fcIpfcBytesRcvd.value, 1, formatBuf[5], 32),
                      formatBytes(el->fcCounters->fcSwilsBytesRcvd.value, 1, formatBuf[6], 32),
                      formatBytes(el->fcCounters->otherFcBytesRcvd.value, 1, formatBuf[7], 32));
	break;
      case (100*showSentReceived + SORT_FC_THPT):
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>"
		      "%s%s"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>",
		      getRowColor(),
		      makeVsanLink (el->fcCounters->vsanId, FLAG_HOSTLINK_HTML_FORMAT,
				    vsanBuf, sizeof(vsanBuf)),
		      webHostName,
		      formatThroughput(el->actualTThpt, 1, formatBuf[0], 32),
		      formatThroughput(el->averageTThpt, 1, formatBuf[1], 32),
		      formatThroughput(el->peakTThpt, 1, formatBuf[2], 32),
		      el->actualTPktThpt,
		      el->averageTPktThpt,
		      el->peakTPktThpt);
	break;
      case (100*showOnlySent + SORT_FC_THPT):
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>"
		      "%s%s"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>",
		      getRowColor(),
		      makeVsanLink (el->fcCounters->vsanId, FLAG_HOSTLINK_HTML_FORMAT,
				    vsanBuf, sizeof(vsanBuf)),
		      webHostName,
		      formatThroughput(el->actualSentThpt, 1, formatBuf[0], 32),
		      formatThroughput(el->averageSentThpt, 1, formatBuf[1], 32),
		      formatThroughput(el->peakSentThpt, 1, formatBuf[2], 32),
		      el->actualSentPktThpt,
		      el->averageSentPktThpt,
		      el->peakSentPktThpt);
	break;
      case (100*showOnlyReceived + SORT_FC_THPT):
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR "TR_ON" %s>"
		      "%s%s"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>"
		      "<TD "TD_BG" ALIGN=RIGHT>%.1f&nbsp;Pkt/s</TD>",
		      getRowColor(),
		      makeVsanLink (el->fcCounters->vsanId, FLAG_HOSTLINK_HTML_FORMAT,
				    vsanBuf, sizeof(vsanBuf)),
		      webHostName,
		      formatThroughput(el->actualRcvdThpt, 1, formatBuf[0], 32),
		      formatThroughput(el->averageRcvdThpt, 1, formatBuf[1], 32),
		      formatThroughput(el->peakRcvdThpt, 1, formatBuf[2], 32),
		      el->actualRcvdPktThpt,
		      el->averageRcvdPktThpt,
		      el->peakRcvdPktThpt);
	break;
      case SORT_FC_ACTIVITY:
	safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), "<TR  "TR_ON" %s>"
		      "%s%s",
		      getRowColor(),
		      makeVsanLink (el->fcCounters->vsanId, FLAG_HOSTLINK_HTML_FORMAT,
				    vsanBuf, sizeof(vsanBuf)),
		      webHostName);
	sendString(buf);
	printHostThtpShort(el, myGlobals.reportKind, hourId);
	buf[0]='\0';
	break;
      }
      sendString(buf);
      sendString("</TR>\n");

      /* Avoid huge tables */
      if(printedEntries++ > myGlobals.runningPref.maxNumLines)
	break;
    }
  }

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

  switch(reportType) {
  case SORT_FC_THPT:
    break;
  case SORT_FC_ACTIVITY:
    break;
  case SORT_FC_DATA:
    sendString("<P><I>Note: These counters do not include broadcasts and "
	       "will not equal the 'Global Protocol Distribution'</I></P>\n");
    break;
  }

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

  printFooter(reportType);

  addPageIndicator(url, pageNum, numEntries, myGlobals.runningPref.maxNumLines,
		   revertOrder, abs(sortedColumn), -1);

  myGlobals.lastRefreshTime = myGlobals.actTime;
  free(tmpTable);
}

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

void purgeHost(char *serialized_serial) {
  HostSerial theSerial;
  HostTraffic *el;

    printHTMLheader("Host Purge", NULL, 0);
// printFlagedWarning("aaa"); return;

str2serial(&theSerial, serialized_serial, strlen(serialized_serial));

#if 0
  {
    char buf[64];

    traceEvent(CONST_TRACE_INFO, "==> %s [type=%d][serial Len=%d]\n",
	       _addrtostr(&theSerial.value.ipSerial.ipAddress, buf, sizeof(buf)),
	       theSerial.serialType, strlen(serialized_serial));

  }
#endif

  el = findHostBySerial(theSerial, myGlobals.actualReportDeviceId);

  if(!el) {
    printFlagedWarning("Unable to purge the specified host: host not found");
  } else {
    int j, found = 0;

    for(j=FIRST_HOSTS_ENTRY; (!found) && (j<myGlobals.device[myGlobals.actualReportDeviceId].actualHashSize); j++) {
      HostTraffic *el1 = myGlobals.device[myGlobals.actualReportDeviceId].hash_hostTraffic[j];

      while(el1 != NULL) {
	if(el1 == el) {
	  found = 1;
			el->to_be_deleted = 1; /* Delete it at the next run */
	  break;
	}

	el1 = el1->next;
      }
    } /* for */

    if(found) {
      char buf[LEN_GENERAL_WORK_BUFFER];

      safe_snprintf(__FILE__, __LINE__, buf, LEN_GENERAL_WORK_BUFFER,
		    "<center>\n"
		    "<p><font color=\"#FF0000\" size=\"+1\">%s</font></p>\n"
		    "</center>\n", "Host Purged Succesfully");
      sendString(buf);
    } else
      printFlagedWarning("Unable to purge the specified host: internal error");
  }
}

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

void printInterfaceStats() {
  char buf[64];
  time_t now = time(NULL);
  
  sendString(ctime(&now));

  snprintf(buf, sizeof(buf), "%u %u\n", 
	   (unsigned int)myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value,
	   (unsigned int)(myGlobals.device[myGlobals.actualReportDeviceId].ethernetBytes.value
			       - myGlobals.device[myGlobals.actualReportDeviceId].ipBytes.value));
  sendString(buf);
  /* traceEvent(CONST_TRACE_ERROR, "%s", buf); */
}

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

void findHost(char *key) {
  HostTraffic *el=NULL;
  int num = 0;
  char buf[256], buf1[256];

  /* traceEvent(CONST_TRACE_INFO, "----------> findHost(%s)", key ? key : "<null>"); */

  sendString("{ results: [");

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

    if((el == myGlobals.broadcastEntry) || (el->hostNumIpAddress == NULL)) continue;
    
    if((key == NULL) || (key == "")) do_add = 1;
    else if(strstr(el->hostNumIpAddress, key)) do_add = 1;
    else if(strstr(el->ethAddressString, key)) do_add = 1;
    else if(strstr(el->hostResolvedName, key)) do_add = 1;
    
    if(do_add) {      
      char *str;

      if(el->hostResolvedName) str = el->hostResolvedName;
      else if(el->ethAddressString[0] != '\0') str = el->ethAddressString;
      else str = "";

      makeHostLink(el, FLAG_HOSTLINK_TEXT_LITE_FORMAT, 0, 0, buf1, sizeof(buf1));

      safe_snprintf(__FILE__, __LINE__, buf, sizeof(buf), 
		    "%s\n\t{ id: \"%d\", value: \"%s\", info: \"%s\" }",
		    (num > 0) ? "," : "", num, str, buf1);
      sendString(buf);
      num++;
      if(num > 32) break; /* Do not display too many entries */
    }    
  }

  sendString("\n] }\n");
}


syntax highlighted by Code2HTML, v. 0.9.1