Logo Search packages:      
Sourcecode: nessusclient version File versions  Download package

pdf_output.c

/* Nessus
 * Copyright (C) 1998 - 2001 Renaud Deraison
 * Copyright (C) 2004 Intevation GmbH
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * In addition, as a special exception, Renaud Deraison
 * gives permission to link the code of this program with any
 * version of the OpenSSL library which is distributed under a
 * license identical to that listed in the included COPYING.OpenSSL
 * file, and distribute linked combinations including the two.
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.  If you modify
 * this file, you may extend this exception to your version of the
 * file, but you are not obligated to do so.  If you do not wish to
 * do so, delete this exception statement from your version.
 *
 * This code deals with the PDF report output.
 */
 
#include <includes.h>

#include "report.h"
#include "report_utils.h"
#include "error_dialog.h"
#include "globals.h"
#include "preferences.h"
#include "backend.h"
#include "data_mining.h"

#include "nessus_i18n.h"

static char * convert_cr_to_html(char *);
static char * portname_to_ahref(char *, char *);
int arglist_to_plainhtml(int, char *);
void pdf_summary_to_file(FILE *, int, struct arglist *);


/*
 * These makros print the utf8 strings as locale strings to file.
 * Obviously this is not an elegant implementation, but I am currently
 * unsure how and how far this method should/must be extended to other
 * modules.
 * Once it is going to be generalized, the implementation should be
 * made more clever.
 */
#define PRINT(file, x) { char * s = _2l(x); fprintf(file, s); g_free(s); }
#define PRINT1(file, x, v) { char * s = _2l(x); fprintf(file, s, v); g_free(s); }

/*
 * Convert the UTF-8 String to what locale is set to.
 * Unfortunately this is required htmldoc does not honor
 * the UTF-8 encoding setting in the html file.
 */
char * _2l(utfstr)
  char * utfstr;
{
  char * localestr;
  gsize bytes_read, bytes_written;
  GError * error;

  localestr = g_locale_from_utf8(utfstr, -1, &bytes_read,
                                 &bytes_written, &error);

  return localestr;
}

/*
 * Print format string 'fmt' to file with one %s
 * replaced with first timestamp of type = 'type'
 * (optionally host = 'host') from backend 'be'.
 * Doesn't print anything of no timestamp is found.
 *
 * TODO: Format timestamp according to locale settings.
 */
static void
PRINT_timestamp(file, be, fmt, type, host)
 FILE * file;
 int be;
 const char *fmt;
 const char *type;
 const char *host;
{
 struct subset *s;
 char *query;

 if(host)
   query = g_strdup_printf("SELECT date FROM timestamps WHERE "
       "type = '%s' and host = '%s'", type, host);
 else
   query = g_strdup_printf("SELECT date FROM timestamps WHERE "
       "type = '%s'", type);

 s = query_backend(be, query);
 g_free(query);
 if(s && subset_size(s))
 {
  char *output = g_strdup_printf(fmt, subset_value(s));

  PRINT(file, output);
  g_free(output);
 }
 subset_free(s);
}

/*
 * Handy functions
 */

/*
 * print URL linking to CVE/BID/Nessus database to file.
 * '%s' in urlfmt (from preferences) will be replaced by
 * item IDs (or their parts split at minus characters).
 * '%%' will insert a percent character.
 * Everything else after '%' aborts parsing.
 * If urlfmt yields an empty string (e.g. it is only "%"),
 * only the item ID will be printed.
 */
static void
fprint_link(file, key, item)
  FILE *file;
  const char *key;
  const char *item;
{
  char *itemparts = estrdup(item);
  char *itemsrc = itemparts;
  char *urlfmt = estrdup(prefs_get_string(Global, key));
  char *src = urlfmt;
  char *url = emalloc(strlen(urlfmt)+strlen(item)+1);
  char *dst = url;
  char *tmp;

  while((tmp = strchr(src, '%')))
  {
    *tmp = '\0';
    strcpy(dst, src);
    dst += strlen(src);
    src = tmp+1;
    switch(*src)
    {
      case 's':
      if((tmp = strchr(itemsrc, '-')))
        *tmp = '\0';
      strcpy(dst, itemsrc);
      dst += strlen(itemsrc);
      if(tmp)
        itemsrc = tmp+1;
      src++;
      break;
      case '%':
      strcpy(dst++, "%");
      src++;
      break;
      default:
      *src = '\0';
    }
  }
  strcpy(dst, src);

  efree(&itemparts);
  efree(&urlfmt);
  if(*url)
    fprintf(file, "<a href=\"%s\">%s</a>", url, item);
  else
    fprintf(file, "%s", item);
  efree(&url);
}

/* All the cross references (CVE, BID) have the same format - XREF: <num>,...<br> */
static char * 
extract_xref(file, str, key)
 FILE * file;
 char * str, * key;
{
 while(str != NULL && strncmp(str, "<br>", 4) != 0)
   {
    char * e1 = strchr(str, ',');
    char * e2 = strchr(str, '<');
    char tmp = '\0';
    if((e1 > e2) || (e1 == NULL))e1 = e2;
   
   
    if(e1 != NULL)
    {
     tmp = e1[0];
     e1[0] = '\0';
    }
    fprint_link(file, key, str);
    str = e1;
    if(e1 != NULL)
    {
     e1[0] = tmp;
   
     if(tmp == ','){
      fputc(',', file);
      fputc(' ', file);
      str ++;
      str ++;
      }
     else
        fputc('<', file);
    }
   }
  return str;
}
 
static void 
print_data_with_links(file, str, plugin_id)
 FILE * file;
 char * str, * plugin_id;
{
 while(str != NULL && str[0] != '\0')
 {
  if(strncmp(str, "http:", 5) == 0 || strncmp(str, "https:", 6) == 0 )
  {
   char * e1, * e2;
   char tmp = 0;
   
   e1 = strchr(str, ' ');
   e2 = strstr(str, "<br>");
   if((e1 > e2) || (e1 == NULL))e1 = e2;
   
   if(e1 != NULL)
   {
    tmp = e1[0];
    e1[0] = '\0';
   }
   fprintf(file, "<a href=\"%s\">%s</a>", str, str);
   str += strlen(str) - 1;
   if(e1 != NULL)
   {
    e1[0] = tmp;
   }
  }
  else if(strncmp(str, "CVE : ", 6) == 0)
  {
   fprintf(file, "CVE : ");
   str += 6;
   str = extract_xref(file, str, "url_cve");
  }
  else if(strncmp(str, "BID : ", 6) == 0)
  {
  fprintf(file, "BID : ");
  str += 6;
  str = extract_xref(file, str, "url_bid");
  }
  else fputc(str[0], file);
  if(str != NULL)
    str++;
 }
 
 fprintf(file, "Nessus ID : ");
 fprint_link(file, "url_nessus", plugin_id);
}


static char * convert_cr_to_html(str)
 char * str;
{
 int num = 0;
 char * t;
 char * ret;
 int i, j = 0;
 /*
  * Compute the size we'll need
  */
  
  t = str;
  while(t[0])
  {
   if((t[0]=='\n')||(t[0]=='>')||(t[0]=='<'))num++;
   t++;
  }
 
  ret = emalloc(strlen(str)+5*num+1);
  for(i=0, j=0;str[i];i++,j++)
  {
   if(str[i]=='\n'){
      ret[j++]='<';
      ret[j++]='b';
      ret[j++]='r';
      ret[j++]='>';
      ret[j]='\n';
      }
   else if(str[i]=='>') {
      ret[j++]='&';
      ret[j++]='g';
      ret[j++]='t';
      ret[j]=';';
      }
  else if(str[i]=='<')
      {
      ret[j++]='&';
      ret[j++]='l';
      ret[j++]='t';
      ret[j]=';';
      }
  else ret[j] = str[i];
  }
  return ret;
}


   
static char * portname_to_ahref(name, hostname)
 char * name;
 char * hostname;
{
  char *t, *k;

  /*
   * Convert '192.168.1.1' to '192_168_1_1' or
   * 'prof.nessus.org' to 'prof_nessus_org'
   */
  hostname = 
    t = estrdup (hostname) ;
  while ((t = strchr (t, '.')) != 0)
    t [0] = '_' ;
  if (name == 0)
    return hostname ;

  /*
   * Convert 'telnet (21/tcp)' to '21_tcp'
   */
  name =
    k = estrdup (name);
  if ((t = strrchr (k, '(')) != 0) 
    k = t + 1;
  if ((t = strchr (k, ')')) != 0)
    t [0] = '\0' ;
  while ((t = strchr (k, '/')) != 0)
    t [0] = '_' ;
 
  /*
   * append: "name" + "_" + "hostname"
   */
  t = emalloc (strlen (hostname) + strlen (k) + 2);
  strcat (strcat (strcpy (t, hostname), "_"), k);
  efree (&hostname);
  efree (&name);
  return t ;
}



static int exec_htmldoc(char ** argv)
{
 int ret;
 int pid;


 pid = fork();
 if ( pid == 0 )
 {
   int i;
   for ( i = 3 ; i < getdtablesize(); i++ ) close(i);
   ret = execvp("htmldoc", argv);
   if ( ret ) exit(127);
   else exit(0);
 }
 if ( pid < 0 ) show_error(_("Could not fork (out of memory?)"));

 for ( ;; )
 {
  int e;
  errno = 0; 
  e =  waitpid(pid, &ret, 0);
  if ( e < 0 && errno == EINTR ) continue; 
  else break;
 }

 return WEXITSTATUS(ret);
}

  

int 
arglist_to_pdf(be, filename)
 int be;
 char * filename;
{
 char tmpfname[PATH_MAX];
 char * cwd = emalloc(PATH_MAX * sizeof(char));
 int cwd_max = PATH_MAX-1;
 int htmldoc_ret;
 char ** argv = NULL;

 const char *nessus_dir = estrdup(prefs_get_string(Global, "nessus_dir"));

 snprintf(tmpfname, PATH_MAX, "%s/.nessus_%d_pdf",nessus_dir, getpid());
 
 while (!getcwd(cwd, cwd_max))
 {    cwd_max=cwd_max+PATH_MAX;
      cwd = erealloc(cwd, (cwd_max+1) * sizeof(char));
 }
 mkdir(tmpfname,0700);
 chdir(tmpfname);

 /* Write the arglist to plain HTML suitable to be processed by HTMLDoc */
 arglist_to_plainhtml(be, "report.html");

 argv = append_argv(argv,  "htmldoc");
 argv = append_argv(argv,  "--firstpage");
 argv = append_argv(argv,  "p1");
 argv = append_argv(argv,  "--footer");
 argv = append_argv(argv,  "./c");
 argv = append_argv(argv,  "--header");
 argv = append_argv(argv,  "..t");
 argv = append_argv(argv,  "--fontsize");
 argv = append_argv(argv,  "10");
 argv = append_argv(argv,  "--quiet");
 argv = append_argv(argv,  "--webpage");
 argv = append_argv(argv,  "-f");
 argv = append_argv(argv,  filename);
 argv = append_argv(argv,  "report.html");
 htmldoc_ret = exec_htmldoc(argv);

 if (htmldoc_ret != 0) {
   if ( htmldoc_ret == 127 )
      show_error(_("PDF report export failed!\nMaybe HTMLDoc (required for PDF export) is not installed or in search path."));
   else
       show_error(_("PDF report export failed! (htmldoc exit code: %d)"), htmldoc_ret);

   htmldoc_ret = 0;
 }
 /* Clean up */
 unlink("report.html");
 chdir(cwd);
 rmdir(tmpfname);
 efree(&cwd);
 return(htmldoc_ret);
}


int
arglist_to_plainhtml(be, filename)
 int be;
 char *filename;
{
 FILE *file;
 struct arglist *hosts;

 if(!strcmp(filename, "-"))file = stdout;
 else file = fopen(filename, "w");
 if(!file){
      show_error(_("Could not create this file !"));
      perror("fopen ");
      return(-1);
      }

 hosts = backend_convert(be);

 /* Print the Style Sheet Opts and Report Summary */
 pdf_summary_to_file(file, be, hosts);

 fprintf(file, "\n<p>&nbsp;</p>\n<h2>");
 PRINT(file, _("Reports per Host"));
 fprintf(file, "</h2>\n");
 
 /* Loop through hosts and print out their problems "Host List"*/
 while(hosts && hosts->next)
 {
  char * hostname;
  char * port;
  char * desc;
  struct arglist * ports;
  char * href;
  char * name;
  hostname = hosts->name;

  href = portname_to_ahref(NULL, hostname);
  fprintf(file, "\n<h3>%s</h3>\n<a name=\"%s\"></a>\n", hostname, href);
  name = portname_to_ahref("toc", hostname);
  fprintf(file, "<a name=\"%s\"></a>\n", name);
  efree(&name);

  PRINT_timestamp(file, be, _("Scan of this host started at: %s<br>\n"),
      "host_start", hostname);
  PRINT_timestamp(file, be, _("Scan of this host finished at: %s<br>\n"),
      "host_end", hostname);

//  fprintf(file, "\n<h4>Analysis of Host %s</h4>\n", hostname);
  fprintf(file, "<table border=1 bordercolor=\"#c1c1c1\" width=\"100%%\"\n");
  fprintf(file, "       cellpadding=2 cellspacing=0>\n");

  fprintf(file, "\t<tr bgcolor=\"#A2B5CD\">\n");
//  fprintf(file, "\t\t<td width=\"20%%\">Adress of Host</td>\n");
  fprintf(file, "\t\t<td width=\"40%%\">");
  PRINT(file, _("Service (Port)"));
  fprintf(file, "</td>\n");
  fprintf(file, "\t\t<td width=\"60%%\">");
  PRINT(file, _("Issue regarding port"));
  fprintf(file, "</td>\n");
  fprintf(file, "\t</tr>\n");

  ports = arg_get_value(hosts->value, "PORTS");
  if(ports)
  {
     struct arglist * open = ports;
     if(open->next)
     {
  
        while(open && open->next){
          name = portname_to_ahref(open->name, hostname);
        if(name)
        {
            if(arg_get_value(open->value, "REPORT") ||
               arg_get_value(open->value, "INFO") ||
               arg_get_value(open->value, "NOTE")) 
            {
                   fprintf(file, "\t<tr>\n");
                   fprintf(file, "\t\t<td><a href=\"#%s\">%s</a></td>\n",
                              name, open->name);
                   if(arg_get_value(open->value, "REPORT")) {
                  fprintf(file, "\t\t<td><font color=red>");
                  PRINT(file, _("Security hole found"));
                  fprintf(file, "</font></td>\n");
                 }
                   else if(arg_get_value(open->value, "INFO")) {
                  fprintf(file, "\t\t<td>");
                  PRINT(file, _("Security warning(s) found"));
                  fprintf(file, "</td>\n");
                 }
                   else {
                  fprintf(file, "\t\t<td>");
                  PRINT(file, _("Security notes found"));
                  fprintf(file, "</td>\n");
                  fprintf(file, "\t</tr>");
                 }
            }      
              else {    
                 fprintf(file, "\t<tr>\n");
                 fprintf(file, "\t\t<td>%s</td>\n", open->name);
                 fprintf(file, "\t\t<td>");
                 PRINT(file, _("No Information"));
                 fprintf(file, "</td>\n\t</tr>");
            }
            efree(&name);
        }
        else {
            fprintf(file, "\t<tr>\n");
              fprintf(file, "\t\t<td>%s</td>\n", open->name);
            fprintf(file, "\t\t<td>");
            PRINT(file, _("No Information"));
            fprintf(file, "</td>\n\t</tr>");
          }
        open = open->next;
       }
     }
  }
  fprintf(file, "</table>\n");
  fprintf(file, "<font size=-2><a href=\"#_summary\">");
  PRINT(file, _("[ return to summary ]"));
  fprintf(file, "</a></font><br><br>\n");

  fprintf(file, "\n<h4>");
  PRINT1(file, _("Security Issues and Fixes - Host %s"), hostname);
  fprintf(file, "</h4>\n");

  /*
   * Write the summary of the open ports here
   */
   while(ports && ports->next)
   {
    struct arglist * report;
    struct arglist * info;
    struct arglist * note;

    port = ports->name;

    name = portname_to_ahref(ports->name, hostname);

    report = arg_get_value(ports->value, "REPORT");
    info = arg_get_value(ports->value, "INFO");
    note = arg_get_value(ports->value, "NOTE");

    if (report || info || note)
    {
    fprintf(file, "<h4><a name=\"%s\"></a>%s - %s</h4>\n", name, hostname, port);
    fprintf(file, "<table border=1 bordercolor=\"#c1c1c1\" width=\"100%%\"\n");
    fprintf(file, "       cellpadding=2 cellspacing=0>\n");

    if(report)while(report && report->next)
     {
     if(strlen(report->value))
     {
     /*
      * Convert the \n to <p> 
      */
      desc = convert_cr_to_html(report->value);
      fprintf(file, "\t<tr bgcolor=\"#FF5050\">\n");
      fprintf(file, "\t\t<td valign=top>");
      PRINT(file, _("Vulnerability"));
      fprintf(file, "</font>: ");
      fprintf(file, "\t</tr>\n");

      fprintf(file, "\t<tr>\n");
      fprintf(file, "\t\t<td>\n");  
      print_data_with_links(file, desc, report->name);
      fprintf(file, "\n\t\t</td>\t</tr>\n");
      efree(&desc);
     }
      report = report->next;
     }
   if(info)while(info && info->next)
    {
     if(strlen(info->value))
     {
     /*
      * Convert the \n to <p> 
      */
     desc = convert_cr_to_html(info->value); 
     name = portname_to_ahref(ports->name, hostname);
     fprintf(file, "\t<tr bgcolor=\"#FFFFA0\">\n");
     fprintf(file, "\t\t<td valign=top>");
     PRINT(file, _("Warning"));
     fprintf(file, ": ");
     fprintf(file, "\t<tr>\n");
     fprintf(file, "\t\t<td>\n");   
     print_data_with_links(file, desc, info->name);
     fprintf(file, "\n\t\t</td>\t</tr>\n");
     efree(&desc);
     }  
     info = info->next;
    }

   if(note)while(note->next)
    {
     if(strlen(note->value))
     {
     char * name;
     desc = emalloc(strlen(note->value)+1);
     strncpy(desc, note->value, strlen(note->value));
     /*
      * Convert the \n to <p> 
      */
     desc = convert_cr_to_html(note->value); 
     name = portname_to_ahref(ports->name, hostname);
     fprintf(file, "\t<tr bgcolor=\"#ABABE1\">\n");
     fprintf(file, "\t\t<td valign=top>");
     PRINT(file, _("Informational"));
     fprintf(file, ": ");
     fprintf(file, "\t<tr>\n");
     fprintf(file, "\t\t<td>\n");   
     print_data_with_links(file, desc, note->name);
     fprintf(file, "\n\t\t</td>\t</tr>\n");
     efree(&desc);
     }  
     note = note->next;
    }
    fprintf(file, "\t</table>\n");
    fprintf(file, "<font size=-2><a href=\"#%s\">", href);
    PRINT1(file, _("[ return to %s ]"), hostname);
    fprintf(file, "</a></font><br><br>\n");
    efree(&name);
    }
    ports = ports->next;
   }
  hosts = hosts->next;
  efree(&href);
 }
 fprintf(file, "<hr>\n<i>");
 PRINT(file, 
      _("This file was generated by <a href=\"http://www.nessus.org\">Nessus</a>, the free security scanner."));
 fprintf(file, "</i></BODY>\n");
 fprintf(file, "</HTML>\n");
 fclose(file);

 if(hosts)
   arg_free_all(hosts);
 return(0);
}


void pdf_summary_to_file(FILE *file, int be, struct arglist *hosts)
{
 struct arglist * dummy = hosts;

 fprintf(file, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Transitional//EN\">\n");
 fprintf(file, "<HTML>\n");
 fprintf(file, " <HEAD>\n");
 fprintf(file, " <TITLE>");
 PRINT(file, _("Nessus Scan Report"));
 fprintf(file, "</TITLE>\n");
 fprintf(file, " <meta http-equiv=\"Content-Type\" content=\"text/html; ");
 fprintf(file, "       charset=\"iso-8859-1\"\">\n");
 fprintf(file, "</HEAD>\n");
 fprintf(file, "<BODY>\n");

 /*
  * Write a (small) summary Hosts that are up, holes/warnings ect.
  */
 fprintf(file, "<h2>");
 PRINT(file, _("Summary"));
 fprintf(file, "</h2>\n");
 fprintf(file, "<a name=\"_summary\">\n");

 PRINT(file, _("This report gives details on hosts that were tested and issues that were found."));
 PRINT(file, _("Please follow the recommended steps and procedures to eradicate these threats.\n"));
 fprintf(file, "\t<p>\n");
 fprintf(file, "\n");

 PRINT_timestamp(file, be, _("Scan started at: %s<br>\n"), "scan_start", NULL);
 PRINT_timestamp(file, be, _("Scan finished at: %s<br>\n"), "scan_end", NULL);

 fprintf(file, "\n<table border=1 bordercolor=\"#c1c1c1\" \n");
 fprintf(file, "       cellpadding=2 cellspacing=0>\n");
 fprintf(file, "\t<tr bgcolor=\"#A2B5CD\">\n");
 fprintf(file, "\t\t<td width=\"16%%\">");
 PRINT(file, _("Host"));
 fprintf(file, "</td>\n");
 fprintf(file, "\t\t<td width=\"30%%\">");
 PRINT(file, _("Possible Issues"));
 fprintf(file, "</td>\n");
 fprintf(file, "\t\t<td width=\"18%%\">");
 PRINT(file, _("Holes"));
 fprintf(file, "</td>\n");
 fprintf(file, "\t\t<td width=\"18%%\">");
 PRINT(file, _("Warnings"));
 fprintf(file, "</td>\n");
 fprintf(file, "\t\t<td width=\"18%%\">");
 PRINT(file, _("Notes"));
 fprintf(file, "</td>\n");
 while (dummy && dummy->next) {
      int result;
      char * href = portname_to_ahref(NULL, dummy->name);

      /* The host name */
      fprintf(file, "\t<tr>\n");
      fprintf(file, "\t\t<td><a href=\"#%s\">%s</a></td>\n", href, 
                                                 dummy->name);
      efree(&href);
                  
      /* The maximal issue found for this host */
      result = is_there_any_hole(dummy->value);
      if(result == HOLE_PRESENT) {
            fprintf(file, "\t\t<td><font color=red>");
            PRINT(file, _("Security hole(s) found"));
            fprintf(file, "</font></td>\n");
      }
      else if(result == WARNING_PRESENT) {
            fprintf(file, "\t\t<td>");
            PRINT(file, _("Security warning(s) found"));
            fprintf(file, "</td>\n");
      }
      else if(result == NOTE_PRESENT) {
            fprintf(file, "\t\t<td>");
            PRINT(file, _("Security note(s) found"));
            fprintf(file, "</td>\n");
      }
      else {
            fprintf(file, "\t\t<td>");
            PRINT(file, _("No noticeable information found"));
            fprintf(file, "</td>\n");
      }
                        
      /* The numbers of holes, warnings and notes for this host */
      fprintf(file, "\t\t<td align=\"center\">%d</td>\n",
                  number_of_holes_by_host(dummy->value));   
      fprintf(file, "\t\t<td align=\"center\">%d</td>\n",
            number_of_warnings_by_host(dummy->value));
      fprintf(file, "\t\t<td align=\"center\">%d</td>\n",
                  number_of_notes_by_host(dummy->value));
      fprintf(file, "\t</tr>\n");
      dummy = dummy->next;
 }
 fprintf(file, "\t<tr>\n");

 /* Sum up the numbers ... */
 fprintf(file, "\t\t<td>");
 PRINT(file, _("Total"));
 fprintf(file, ": %d</td>\n\t\t<td>&nbsp;</td>\n",
      arglist_length(hosts)); 
 fprintf(file, "\t\t<td align=\"center\">%d</td>\n",
      number_of_holes(hosts));      
 fprintf(file, "\t\t<td align=\"center\">%d</td>\n",
      number_of_warnings(hosts));
 fprintf(file, "\t\t<td align=\"center\">%d</td>\n",
            number_of_notes(hosts));
 fprintf(file, "\t</tr>\n");
 fprintf(file, "</table>\n");
}

Generated by  Doxygen 1.6.0   Back to index