Added SILC Map.
authorPekka Riikonen <priikone@silcnet.org>
Wed, 23 Jul 2003 15:00:50 +0000 (15:00 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Wed, 23 Jul 2003 15:00:50 +0000 (15:00 +0000)
13 files changed:
apps/silcmap/Makefile.am [new file with mode: 0644]
apps/silcmap/README [new file with mode: 0644]
apps/silcmap/data.h [new file with mode: 0644]
apps/silcmap/default.fnt [new file with mode: 0644]
apps/silcmap/example.server.com_706.pub [new file with mode: 0644]
apps/silcmap/silcmap.c [new file with mode: 0644]
apps/silcmap/silcmap.conf [new file with mode: 0644]
apps/silcmap/silcmap.h [new file with mode: 0644]
apps/silcmap/silcmap_bitmap.c [new file with mode: 0644]
apps/silcmap/silcmap_client.c [new file with mode: 0644]
apps/silcmap/silcmap_command.c [new file with mode: 0644]
apps/silcmap/silcmap_html.c [new file with mode: 0644]
configure.in.pre

diff --git a/apps/silcmap/Makefile.am b/apps/silcmap/Makefile.am
new file mode 100644 (file)
index 0000000..fee16ac
--- /dev/null
@@ -0,0 +1,34 @@
+#
+#  Makefile.am
+#
+#  Author: Pekka Riikonen <priikone@silcnet.org>
+#
+#  Copyright (C) 2003 Pekka Riikonen
+#
+#  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; version 2 of the License.
+#
+#  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.
+#
+
+AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign
+
+bin_PROGRAMS = silcmap
+
+silcmap_SOURCES =              \
+       silcmap.c               \
+       silcmap_bitmap.c        \
+       silcmap_command.c       \
+       silcmap_client.c        \
+       silcmap_html.c
+
+LIBS = $(SILC_COMMON_LIBS) -lsilcclient -lsilc
+LDADD =
+
+EXTRA_DIST = *.h
+
+include $(top_srcdir)/Makefile.defines.in
diff --git a/apps/silcmap/README b/apps/silcmap/README
new file mode 100644 (file)
index 0000000..154d778
--- /dev/null
@@ -0,0 +1,46 @@
+The SILC Map
+============
+
+SILC Map is a utility which can be used to visualize the topology of a
+SILC network.  It can create maps which indicate the exact global position
+of the servers and it can create HTML pages out of the information it 
+gathers from the servers.  It is also possible to create a HTML map 
+page which allows the user to click the servers on the map image to get
+more detailed information of the server.  The links between the routers
+and servers can also be drawn on the map image.
+
+The HTML pages that SILC Map creates are by default crude looking.  This
+is intentional feature to allow the user to easily embed the data pages
+into existing web site.  This can be done for example by using PHP, server
+side includes or similar method.
+
+
+SILC Map Config File
+====================
+
+By default the SILC Map reads the silcmap.conf file.  Please read the
+example silcmap.conf file to learn all the possible commands and 
+configuration options you can use to alter the behavior of the SILC Map.
+
+
+Running the SILC Map
+====================
+
+After you have configured the silcmap.conf file, simply run the ./silcmap
+program.  It reads the file, the source map image, creates the connections
+to SILC servers and producess the target image and HTML pages.  After the
+map is created the silcmap program exits.  If you want to periodically run 
+the silcmap to generate maps you may create for example an at (see at(1)) 
+job on your local system.
+
+
+Contact
+=======
+
+Feedback and comments are welcome.  Bug reports should be sent to the
+SILC development mailing list.
+
+Official SILC project web site      : http://silcnet.org/
+FTP archive for SILC project        : ftp://ftp.silcnet.org/
+Development mailing list address    : silc-devel@lists.silcnet.org
+
diff --git a/apps/silcmap/data.h b/apps/silcmap/data.h
new file mode 100644 (file)
index 0000000..cf0788e
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+
+  data.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 Pekka Riikonen
+
+  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; version 2 of the License.
+
+  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.
+
+*/
+
+#ifndef DATA_H
+#define DATA_H
+
+/* Bitmap context */
+typedef struct {
+  char width;
+  char height;
+  unsigned char data[32 * 32];
+} MapBitmap;
+
+/* Circle */
+const MapBitmap silc_map_circle =
+{
+  6, 7,
+  {  0,  1,  1,  1,  1,  0,
+     1,  0,  0,  0,  0,  1,
+     1,  0,  0,  0,  0,  1,
+     1,  0,  0,  0,  0,  1,
+     1,  0,  0,  0,  0,  1,
+     1,  0,  0,  0,  0,  1,
+     0,  1,  1,  1,  1,  0
+  }
+};
+
+/* Rectangle */
+const MapBitmap silc_map_rectangle =
+{
+  6, 7,
+  {  1,  1,  1,  1,  1,  1,
+     1,  0,  0,  0,  0,  1,
+     1,  0,  0,  0,  0,  1,
+     1,  0,  0,  0,  0,  1,
+     1,  0,  0,  0,  0,  1,
+     1,  0,  0,  0,  0,  1,
+     1,  1,  1,  1,  1,  1
+  }
+};
+
+#endif /* DATA_H */
diff --git a/apps/silcmap/default.fnt b/apps/silcmap/default.fnt
new file mode 100644 (file)
index 0000000..cf6afb4
Binary files /dev/null and b/apps/silcmap/default.fnt differ
diff --git a/apps/silcmap/example.server.com_706.pub b/apps/silcmap/example.server.com_706.pub
new file mode 100644 (file)
index 0000000..80ed283
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN SILC PUBLIC KEY-----
+AAAA/QADcnNhAGZVTj1zaWxjLW9wZXIsIEhOPXNpbGMuc2lsY25ldC5vcmcsIFJOPVNJTEM
+gUm91dGVyIEFkbWluLCBFPXNpbGMtb3BlckBzaWxjbmV0Lm9yZywgTz1TSUxDIFByb2plY3
+QsIEM9U0sAAAAEAAAAfwAAAIAhKuTYfWdQ5UxB6ICuYCO13+gFIXZne3ciTm4c0AiHUoRat
+5Pd116FhO2ktkVZC8QS6IfRso5BkaYtRyp7qsficnXxZmFH5nBctXvlZ5zFWiDLsDz6TKI0
+5ib/T9EyRblNIpzQVrJsopvrz0mYmZKxUuu/e06EbdvpoubeHj6xLQ==
+-----END SILC PUBLIC KEY-----
diff --git a/apps/silcmap/silcmap.c b/apps/silcmap/silcmap.c
new file mode 100644 (file)
index 0000000..6e0a16d
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+
+  silcmap.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 Pekka Riikonen
+
+  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; version 2 of the License.
+
+  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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "silcversion.h"
+#include "silcmap.h"
+
+/* Allocates new SilcMap context and the SilcClient in it. */
+
+SilcMap silc_map_alloc(const char *conffile)
+{
+  SilcMap map = silc_calloc(1, sizeof(*map));
+  if (!map)
+    return NULL;
+
+  /* Allocate client */
+  map->client = silc_client_alloc(&silc_map_client_ops, NULL, NULL, NULL);
+  if (!map->client) {
+    silc_free(map);
+    return NULL;
+  }
+
+  map->client->username = strdup("silcmap");
+  map->client->hostname = silc_net_localhost();
+  map->client->realname = strdup("silcmap");
+
+  /* Init the client */
+  if (!silc_client_init(map->client)) {
+    silc_client_free(map->client);
+    silc_free(map);
+    return NULL;
+  }
+
+  /* Load new key pair if it exists, create if it doesn't. */
+  if (!silc_load_key_pair("silcmap.pub", "silcmap.prv", "",
+                         &map->client->pkcs,
+                         &map->client->public_key,
+                         &map->client->private_key)) {
+    /* The keys don't exist.  Let's generate us a key pair then!  There's
+       nice ready routine for that too.  Let's do 1024 bit RSA key pair. */
+    if (!silc_create_key_pair("rsa", 1024, "silcmap.pub",
+                             "silcmap.prv", NULL, "",
+                             &map->client->pkcs,
+                             &map->client->public_key,
+                             &map->client->private_key, FALSE)) {
+      fprintf(stderr, "Could not create new key pair");
+      silc_client_free(map->client);
+      silc_free(map);
+      return NULL;
+    }
+  }
+
+  map->conffile = strdup(conffile);
+
+  return map;
+}
+
+/* Free the SilcMap context and all data in it. */
+
+void silc_map_free(SilcMap map)
+{
+  SilcMapConnection mapconn;
+  SilcMapCommand cmd;
+  char *h;
+
+  silc_free(map->conffile);
+  silc_free(map->bitmap);
+
+  if (map->client) {
+    silc_free(map->client->username);
+    silc_free(map->client->realname);
+    silc_free(map->client->hostname);
+    silc_client_free(map->client);
+  }
+
+  if (map->conns) {
+    silc_dlist_start(map->conns);
+    while ((mapconn = silc_dlist_get(map->conns)) != SILC_LIST_END) {
+      silc_dlist_start(mapconn->hostnames);
+      while ((h = silc_dlist_get(mapconn->hostnames)) != SILC_LIST_END)
+       silc_free(h);
+      silc_dlist_uninit(mapconn->hostnames);
+
+      silc_dlist_start(mapconn->ips);
+      while ((h = silc_dlist_get(mapconn->ips)) != SILC_LIST_END)
+       silc_free(h);
+      silc_dlist_uninit(mapconn->ips);
+
+      silc_dlist_start(mapconn->commands);
+      while ((cmd = silc_dlist_get(mapconn->commands)) != SILC_LIST_END) {
+       silc_free(cmd->filename);
+       silc_free(cmd->text);
+       silc_free(cmd);
+      }
+      silc_dlist_uninit(mapconn->commands);
+
+      silc_free(mapconn->public_key);
+      silc_free(mapconn->country);
+      silc_free(mapconn->city);
+      silc_free(mapconn->admin);
+      silc_free(mapconn->description);
+      silc_free(mapconn->writemaphtml_url);
+      silc_free(mapconn->up_color);
+      silc_free(mapconn->up_text_color);
+      silc_free(mapconn->down_color);
+      silc_free(mapconn->down_text_color);
+      silc_free(mapconn->data.motd);
+      silc_free(mapconn);
+    }
+    silc_dlist_uninit(map->conns);
+  }
+
+  silc_free(map->writemap.filename);
+  silc_free(map->writehtml.filename);
+  silc_free(map->writemaphtml.filename);
+  silc_free(map->writemaphtml.text);
+  silc_free(map->cut.filename);
+
+  silc_free(map);
+}
+
+/* Starts the actual silcmap by parsing the commands script. */
+
+SILC_TASK_CALLBACK(silc_map_start)
+{
+  SilcMap map = context;
+
+  /* Load default font */
+  silc_map_load_font(map, "default.fnt");
+
+  /* Start command parsing.  Most of the commands are executed when they
+     are parsed so most of the real magic happens here. */
+  if (!silc_map_commands_parse(map, map->conffile)) {
+    /* Program stops */
+    silc_schedule_stop(map->client->schedule);
+  }
+}
+
+/* Long command line options */
+static struct option long_opts[] =
+{
+  { "config-file", 1, NULL, 'f' },
+  { "debug", 2, NULL, 'd' },
+  { "help", 0, NULL, 'h' },
+  { "version", 0, NULL,'V' },
+
+  { NULL, 0, NULL, 0 }
+};
+
+static void silc_map_usage(void)
+{
+  printf(""
+"Usage: silcmap [options]\n"
+"\n"
+"  Generic Options:\n"
+"  -f  --config-file=FILE        Alternate SILC Map configuration file\n"
+"  -d  --debug=string            Enable debugging\n"
+"  -h  --help                    Display this message and exit\n"
+"  -V  --version                 Display version and exit\n"
+"\n");
+  exit(0);
+}
+
+int main(int argc, char **argv)
+{
+  SilcMap map;
+  int opt, option_index;
+  char *filename = NULL;
+
+  if (argc > 1) {
+    while ((opt = getopt_long(argc, argv, "f:d:hV",
+                             long_opts, &option_index)) != EOF) {
+      switch(opt) {
+       case 'h':
+         silc_map_usage();
+         break;
+       case 'V':
+         printf("SILC Map, version %s\n", silc_dist_version);
+         printf("(c) 2003 Pekka Riikonen <priikone@silcnet.org>\n");
+         exit(0);
+         break;
+       case 'd':
+#ifdef SILC_DEBUG
+         silc_debug = TRUE;
+         silc_debug_hexdump = TRUE;
+         if (optarg)
+           silc_log_set_debug_string(optarg);
+         silc_log_quick = TRUE;
+#else
+         fprintf(stderr,
+                 "Run-time debugging is not enabled. To enable it recompile\n"
+                 "the server with --enable-debug configuration option.\n");
+#endif
+         break;
+       case 'f':
+         filename = strdup(optarg);
+         break;
+       default:
+         silc_map_usage();
+         break;
+      }
+    }
+  }
+
+  /* Allocate map context */
+  if (!filename)
+    filename = strdup("silcmap.conf");
+  map = silc_map_alloc(filename);
+  if (!map)
+    return 1;
+
+  /* Schedule for command script parsing */
+  silc_schedule_task_add(map->client->schedule, 0,
+                        silc_map_start, map, 0, 1,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+
+  /* Run the silcmap client */
+  silc_client_run(map->client);
+
+  /* Cleanup */
+  silc_client_stop(map->client);
+  silc_map_free(map);
+  silc_free(filename);
+
+  return 0;
+}
diff --git a/apps/silcmap/silcmap.conf b/apps/silcmap/silcmap.conf
new file mode 100644 (file)
index 0000000..c8a85ab
--- /dev/null
@@ -0,0 +1,141 @@
+#
+# Map drawing script file
+#
+# Command     : server { ... }
+# Description : Specifies a server to connect, fetch statistics, draw the
+#               server on map and to show its status on the map.  See the
+#               example server command below for all parameters of this
+#               command.
+#
+# Command     : loadmap { filename = <filename>; };
+# Example     : loadmap { filename = "world_map.ppm"; };
+# Description : Load the source bitmap image file to be used as the map
+#
+# Command     : writemap { filename = <filename>; };
+# Example     : writemap { filename = "map.ppm"; };
+# Description : Write the target bitmap image file
+#
+# Command     : writemaphtml { filename = <filename>; image = <filename>;
+#                              cut_lat = <lat>; cut_lon = <lon>; };
+# Example     : writemaphtml { filename = "htmlmap.map"; image = "map.jpg"; };
+# Description : Write the gathered information as as HTML map page.  This
+#              allows the user to click the specified points of URLs.
+#               The writemaphtml_url parameter in server will specify the 
+#               URL that will be used in the HTML map.  If omitted the
+#               filename created by writehml command is used.  The <cut_lat>
+#               and <cut_lon> are specified if the <image> is a portion of
+#               the original map image.  In this case the lat and lon specify
+#               which portion it is.
+#
+# Command     : writehtml { filename = <filename>; };
+# Example     : writehtml { filename = "index.html"; };
+# Description : Write the gathered information as HTML pages.  Every server
+#               command will have their own HTML file.  The filename is
+#               based on the hostname of the server.  The index HTML file
+#               will include list of the servers.  The generated HTML files
+#               are quite raw and are intended to be embedded into user's
+#               own website.
+#
+# Command     : cut { lat = <lat>; lon = <lon>; width = <width>; 
+#                     height = <height>; filename = <filename>; };
+# Example     : cut { lat = "20"; lon = "5"; width = "1000"; height = "700";
+#                     filename = "map_chunk.ppm"; };
+# Description : Cuts a chunk from the source image at specified location.
+#               The chunk will be <width> * <height> pixels in size.  The
+#               <filename> is the output bitmap file where the chunk is saved.
+#
+# Command     : rectangle { lat = <lat>; lon = <lon>; color = <color>; 
+#                           label = <label>; lposx = <x>; lposy = <y>; 
+#                           lcolor = <label color>; };
+# Example     : rectangle { lat = "-31 30"; lon = "0"; color = "0 0 255"; 
+#                           label = "text"; lposx = "-10", lposy = "15"; };
+# Description : Draws a rectangle at specified location.  The center of the
+#               rectangle will be at specified location.  If the <label>
+#               is specified, that text will appear with the rectangle at the
+#               <lposx> and <lposy> pixel location in relation to the
+#               rectangle.  If <lcolor> is omitted the <color> is used.
+#
+# Command     : circle { lat = <lat>; lon = <lon>; color = <color>;
+#                        label = <label>; lposx = <x>; lposy = <y>; 
+#                        lcolor = <label color>; };
+# Example     : circle { lat = "31 30"; lon = "0"; color = "255 255 255"; };
+# Description : Draws a circle at specified location.  The center of the
+#               circle will be at specified location.  If the <label>
+#               is specified, that text will appear with the circle at the
+#               <lposx> and <lposy> pixel location in relation to the 
+#               circle.  If <lcolor> is omitted the <color> is used.
+#
+# Command     : line { a_lat = <lat>; a_lon = <lon>; b_lat = <lat>;
+#                      b_lon = <lon>; width = <width>; color = <color>; };
+# Description : Draws a straight line between points a and b.  The <width>
+#               is line width in pixels.  If omitted, default is 1.
+#
+# Command     : text { lat = <lat>; lon = <lon>; color = <color>;
+#                      text = <text>; };
+# Example     : text { lat = "63 42 13", lon = "23 17 34", color = "0 0 255";
+#                      text = "This is an example text"; };
+# Description : Prints the text <text> at the specified location
+#
+# The <lat> is in format DD MM SS, where DD is degrees, MM minutes, and
+# SS seconds.  If DD is positive the latitude is north bound, if negative 
+# it is south bound.  At least DD must be specified.
+#
+# The <lon> is in format DD MM SS, where DD is degrees, MM minutes, and
+# SS seconds.  If DD is positive the longitude is east bound, if negative 
+# it is west bound.  At least DD must be specified.
+#
+# The <color> is in format RRR GGG BBB, where RRR is red, GGG green and
+# BBB blue, between 0 - 255.
+#
+
+loadmap   { filename = "map.ppm"; };
+writemaphtml { filename = "map.html"; image = "map.jpg"; };
+
+server {
+       # Server details
+       hostname        = "eample.server.com";
+       ip              = "10.2.1.1";
+       port            = "706";
+       public_key      = "example.server.com_706.pub";
+       country         = "Finland";
+       city            = "Kuopio";
+       admin           = "pekka";
+       description     = "";
+
+       # Connection parameters
+       connect         = false;
+       connect_timeout = "30";
+
+       # Statistics to fetch
+       starttime       = true;
+       uptime          = true;
+       clients         = true;
+       channels        = true;
+       server_ops      = true;
+       router_ops      = true;
+       cell_clients    = true;
+       cell_channels   = true;
+       cell_servers    = true;
+       all_clients     = true;
+       all_channels    = true;
+       all_servers     = true;
+       all_routers     = true;
+       all_server_ops  = true;
+       all_router_ops  = true;
+
+       # Fetch message of the day
+       motd            = true;
+
+       # Status colors when server is either up or down
+       up_color        = "150 200 190";
+       down_color      = "150 0 0";
+       up_text_color   = "60 200 200";
+       down_text_color = "150 0 0";
+
+       # Draw this server on the map
+       rectangle { lat = "36 00"; lon = "25 00";
+                   label = "example.server.com";
+                   lposx = "-30"; lposy = "10"; };
+};
+
+writemap  { filename = "testi2.ppm"; };
diff --git a/apps/silcmap/silcmap.h b/apps/silcmap/silcmap.h
new file mode 100644 (file)
index 0000000..e0996dd
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+
+  silcmap.h
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 Pekka Riikonen
+
+  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; version 2 of the License.
+
+  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.
+
+*/
+
+#ifndef SILCMAP_H
+#define SILCMAP_H
+
+/* Font context */
+typedef struct {
+  int height;
+  struct {
+    char width;
+    unsigned char data[16 * 16];
+  } font[94];
+} MapFonts;
+
+/* SilcMap command context. */
+typedef struct {
+  /* Map command datas */
+  char *filename;
+  SilcInt32 x;
+  SilcInt32 y;
+  SilcInt32 x2;
+  SilcInt32 y2;
+  SilcUInt32 width;
+  SilcUInt32 height;
+  char *text;
+  SilcInt32 lposx;
+  SilcInt32 lposy;
+
+  SilcInt16 r;
+  SilcInt16 g;
+  SilcInt16 b;
+  SilcInt16 lr;
+  SilcInt16 lg;
+  SilcInt16 lb;
+  unsigned int color_set      : 1;
+  unsigned int lcolor_set     : 1;
+
+  /* Command */
+  unsigned int writemap       : 1;
+  unsigned int writehtml      : 1;
+  unsigned int writemaphtml   : 1;
+  unsigned int cut            : 1;
+  unsigned int draw_line      : 1;
+  unsigned int draw_text      : 1;
+  unsigned int draw_circle    : 1;
+  unsigned int draw_rectangle : 1;
+} *SilcMapCommand, SilcMapCommandStruct;
+
+/* The SilcMap context. */
+typedef struct {
+  /* Connection data */
+  SilcClient client;           /* SILC Client context */
+  char *conffile;              /* Config file name */
+  SilcDList conns;             /* Connections */
+  SilcUInt32 conns_num;                /* Number of created connections */
+  SilcUInt32 conn_num;         /* Current number of processed connections */
+
+  /* Bitmap data */
+  unsigned char *bitmap;       /* Loaded bitmap image */
+  SilcUInt32 bitsilc_map_size; /* Size of bitmap */
+  SilcUInt32 width;            /* Bitmap width in pixels */
+  SilcUInt32 height;           /* Bitmap height in pixels */
+  SilcUInt32 maxcolor;         /* Max color value in bitmap */
+  MapFonts font;               /* Current font */
+
+  /* Output methods */
+  SilcMapCommandStruct writemap;
+  SilcMapCommandStruct writehtml;
+  SilcMapCommandStruct writemaphtml;
+  SilcMapCommandStruct cut;
+} *SilcMap;
+
+/* SilcMap connecetion context. */
+typedef struct {
+  /* Server and connection details */
+  SilcDList hostnames;
+  SilcDList ips;
+  int port;
+  char *public_key;
+  char *country;
+  char *city;
+  char *admin;
+  char *description;
+  int connect_timeout;
+  char *writemaphtml_url;
+
+  /* Flags */
+  unsigned int connect        : 1;
+  unsigned int starttime      : 1;
+  unsigned int uptime         : 1;
+  unsigned int clients        : 1;
+  unsigned int channels       : 1;
+  unsigned int server_ops     : 1;
+  unsigned int router_ops     : 1;
+  unsigned int cell_clients   : 1;
+  unsigned int cell_channels  : 1;
+  unsigned int cell_servers   : 1;
+  unsigned int all_clients    : 1;
+  unsigned int all_channels   : 1;
+  unsigned int all_servers    : 1;
+  unsigned int all_routers    : 1;
+  unsigned int all_server_ops : 1;
+  unsigned int all_router_ops : 1;
+  unsigned int motd           : 1;
+  unsigned int down           : 1;
+  unsigned int stats_received : 1;
+  unsigned int motd_received  : 1;
+
+  /* Gathered data */
+  struct {
+    SilcUInt32 starttime;
+    SilcUInt32 uptime;
+    SilcUInt32 clients;
+    SilcUInt32 channels;
+    SilcUInt32 server_ops;
+    SilcUInt32 router_ops;
+    SilcUInt32 cell_clients;
+    SilcUInt32 cell_channels;
+    SilcUInt32 cell_servers;
+    SilcUInt32 all_clients;
+    SilcUInt32 all_channels;
+    SilcUInt32 all_servers;
+    SilcUInt32 all_routers;
+    SilcUInt32 all_server_ops;
+    SilcUInt32 all_router_ops;
+    char *motd;
+  } data;
+
+  /* Status colors */
+  char *up_color;
+  char *down_color;
+  char *up_text_color;
+  char *down_text_color;
+
+  /* Map commands */
+  SilcDList commands;
+
+  /* Back pointers */
+  SilcMap map;
+  SilcClientConnection conn;
+} *SilcMapConnection;
+
+extern SilcClientOperations silc_map_client_ops;
+
+SilcMap silc_map_alloc(const char *conffile);
+void silc_map_free(SilcMap map);
+bool silc_map_commands_parse(SilcMap map, const char *filename);
+void silc_map_connect(SilcMap map, SilcMapConnection mapconn);
+bool silc_map_load_ppm(SilcMap map, const char *filename);
+bool silc_map_write_ppm(SilcMap map, const char *filename);
+bool silc_map_cut(SilcMap map, SilcInt32 x, SilcInt32 y,
+                 SilcUInt32 width, SilcUInt32 height,
+                 SilcMap *ret_map);
+bool silc_map_draw(SilcMap map,
+                  SilcInt32 x, SilcInt32 y,
+                  const unsigned char *bitmap,
+                  SilcUInt32 width, SilcUInt32 height);
+bool silc_map_draw_raw(SilcMap map,
+                      SilcInt32 x, SilcInt32 y,
+                      const unsigned char *bitmap,
+                      SilcUInt32 width, SilcUInt32 height,
+                      SilcInt16 r, SilcInt16 g, SilcInt16 b);
+bool silc_map_draw_line(SilcMap map, SilcUInt32 width,
+                       SilcInt32 a_x, SilcInt32 a_y,
+                       SilcInt32 b_x, SilcInt32 b_y,
+                       SilcInt16 r, SilcInt16 g, SilcInt16 b);
+bool silc_map_draw_text(SilcMap map, const char *text,
+                       SilcInt32 x, SilcInt32 y,
+                       SilcInt16 r, SilcInt16 g, SilcInt16 b);
+bool silc_map_draw_circle(SilcMap map, SilcInt32 x, SilcInt32 y,
+                         SilcInt16 r, SilcInt16 g, SilcInt16 b,
+                         const char *label, SilcInt32 lposx, SilcInt32 lposy,
+                         SilcInt16 lr, SilcInt16 lg, SilcInt16 lb);
+bool silc_map_draw_rectangle(SilcMap map, SilcInt32 x, SilcInt32 y,
+                            SilcInt16 r, SilcInt16 g, SilcInt16 b,
+                            const char *label, SilcInt32 lposx,
+                            SilcInt32 lposy,
+                            SilcInt16 lr, SilcInt16 lg, SilcInt16 lb);
+double silc_map_parse_pos(char *pos);
+int silc_map_lon2x(SilcMap map, char *latitude);
+int silc_map_lat2y(SilcMap map, char *longitude);
+bool silc_map_parse_color(const char *color,
+                         SilcInt16 *r, SilcInt16 *g, SilcInt16 *b);
+bool silc_map_load_font(SilcMap map, const char *filename);
+bool silc_map_writehtml(SilcMap map, SilcMapConnection mapconn);
+bool silc_map_writehtml_index(SilcMap map);
+bool silc_map_writemaphtml(SilcMap map);
+
+#endif /* SILCMAP_H */
diff --git a/apps/silcmap/silcmap_bitmap.c b/apps/silcmap/silcmap_bitmap.c
new file mode 100644 (file)
index 0000000..ea4c92c
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+
+  silcmap_bitmap.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 Pekka Riikonen
+
+  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; version 2 of the License.
+
+  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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include <math.h>
+#include "silcmap.h"
+#include "data.h"
+
+/******* Bitmap Routines *****************************************************/
+
+/* Load a bitmap file.  The file is the map image that is loaded into
+   the SilcMap context.  This is no perfect PPM loader. */
+
+bool silc_map_load_ppm(SilcMap map, const char *filename)
+{
+  FILE *fp;
+  char type[3];
+  int ret, retval = TRUE;
+
+  SILC_LOG_DEBUG(("Load PPM '%s'", filename));
+
+  fp = fopen(filename, "r");
+  if (!fp) {
+    fprintf(stderr, "fopen: %s\n", strerror(errno));
+    return FALSE;
+  }
+
+  /* Read width and height */
+  ret = fscanf(fp, "%s %ld %ld %ld ",
+              type, &map->width, &map->height, &map->maxcolor);
+  if (ret < 4) {
+    fprintf(stderr, "fscanf: %s\n", strerror(errno));
+    retval = FALSE;
+    goto out;
+  }
+
+  /* Read the picture */
+  map->bitsilc_map_size = map->width * 3 * map->height;
+  map->bitmap = silc_malloc(map->bitsilc_map_size);
+  ret = fread(map->bitmap, map->bitsilc_map_size, 1, fp);
+  if (ret < 0) {
+    fprintf(stderr, "fscanf: %s\n", strerror(errno));
+    retval = FALSE;
+    goto out;
+  }
+
+ out:
+  fclose(fp);
+  return retval;
+}
+
+/* Write the map into a bitmap file. */
+
+bool silc_map_write_ppm(SilcMap map, const char *filename)
+{
+  FILE *fp;
+  int retval = TRUE;
+  int i, k;
+
+  SILC_LOG_DEBUG(("Write PPM '%s'", filename));
+
+  fp = fopen(filename, "w+");
+  if (!fp) {
+    fprintf(stderr, "fopen: %s\n", strerror(errno));
+    return FALSE;
+  }
+
+  /* Write the header */
+  fprintf(fp, "P6 %ld %ld %ld\n", map->width, map->height, map->maxcolor);
+
+  /* Write the bitmap */
+  for (i = 0; i < map->height; i++)
+    for (k = 0; k < map->width; k++) {
+      putc(map->bitmap[i * (map->width * 3) + (k * 3)    ], fp);   /* R */
+      putc(map->bitmap[i * (map->width * 3) + (k * 3) + 1], fp);   /* G */
+      putc(map->bitmap[i * (map->width * 3) + (k * 3) + 2], fp);   /* B */
+    }
+
+  fclose(fp);
+  return retval;
+}
+
+/* Cut the map into a `width' * `height' size chunk at `x' and `y'.  This
+   returns the allocated map bitmap into `ret_bitmap'.  The original map
+   is not modified. */
+
+bool silc_map_cut(SilcMap map, SilcInt32 x, SilcInt32 y,
+                 SilcUInt32 width, SilcUInt32 height,
+                 SilcMap *ret_map)
+{
+  int i;
+
+  SILC_LOG_DEBUG(("cut"));
+
+  /* Sanity checks */
+  if (height > map->height - y) {
+    fprintf(stderr, "Requesting too much height: %ld\n", height);
+    return FALSE;
+  }
+  if (width > map->width - x) {
+    fprintf(stderr, "Requesting too much width: %ld\n", width);
+    return FALSE;
+  }
+
+  /* Compute coordinates in the bitmap */
+  y = (map->width * 3) * y;
+  x = (x * 3);
+
+  /* Allocate new SilcMap context */
+  *ret_map = silc_calloc(1, sizeof(**ret_map));
+  (*ret_map)->width = width;
+  (*ret_map)->height = height;
+  (*ret_map)->maxcolor = map->maxcolor;
+  (*ret_map)->bitsilc_map_size = (width * 3) * height;
+  (*ret_map)->bitmap = silc_malloc((*ret_map)->bitsilc_map_size);
+
+  /* Copy the requested area */
+  for (i = 0; i < height; i++) {
+    memcpy((*ret_map)->bitmap + (i * width * 3),
+          map->bitmap + y + x, width * 3);
+
+    /* Next line */
+    y += (map->width * 3);
+  }
+
+  return TRUE;
+}
+
+/* Draw a bitmap indicated by `bitmap' of size of `width' * 'height'
+   into the SilcMap context into the coordinates `x' and `y' (the upper left
+   corner of the bitmap will be at x and y).  The `bitmap' must be RGB
+   color bitmap. */
+
+bool silc_map_draw(SilcMap map,
+                  SilcInt32 x, SilcInt32 y,
+                  const unsigned char *bitmap,
+                  SilcUInt32 width, SilcUInt32 height)
+{
+  int i, k;
+  unsigned char val;
+
+  /* Compute coordinates in the bitmap */
+  y = (map->width * 3) * y;
+  x = (x * 3);
+
+  /* Draw the bitmap into the map bitmap */
+  for (i = 0; i < height; i++) {
+    for (k = 0; k < width; k++) {
+      val = bitmap[i * (width * 3) + (k * 3)];
+      map->bitmap[y + x + (k * 3)    ] = val;            /* R */
+
+      val = bitmap[i * (width * 3) + (k * 3) + 1];
+      map->bitmap[y + x + (k * 3) + 1] = val;            /* G */
+
+      val = bitmap[i * (width * 3) + (k * 3) + 2];
+      map->bitmap[y + x + (k * 3) + 2] = val;            /* B */
+    }
+
+    /* Next line */
+    y += (map->width * 3);
+  }
+
+  return TRUE;
+}
+
+/* Same as silc_map_draw but the `bitmap' is a grayscale bitmap
+   and the RGB color information is provided as argument to this function. */
+
+bool silc_map_draw_raw(SilcMap map,
+                      SilcInt32 x, SilcInt32 y,
+                      const unsigned char *bitmap,
+                      SilcUInt32 width, SilcUInt32 height,
+                      SilcInt16 r, SilcInt16 g, SilcInt16 b)
+{
+  int i, k;
+  unsigned char val;
+
+  /* Compute coordinates in the bitmap */
+  y = (map->width * 3) * y;
+  x = (x * 3);
+
+  /* Draw the bitmap into the map bitmap */
+  for (i = 0; i < height; i++) {
+    for (k = 0; k < width; k++) {
+      val = bitmap[i * width + k];
+      if (val != 0) {
+        map->bitmap[y + x + (k * 3)    ] = r;            /* R */
+        map->bitmap[y + x + (k * 3) + 1] = g;            /* G */
+        map->bitmap[y + x + (k * 3) + 2] = b;            /* B */
+      }
+    }
+
+    /* Next line */
+    y += (map->width * 3);
+  }
+
+  return TRUE;
+}
+
+/* Draw a straight line between points a and b.  The coordinates for the
+   points are provided as arguments.  The `width' is the line width in
+   pixels.  The RGB color for the line can be provided too.  Implements
+   DDA algorithm. */
+
+bool silc_map_draw_line(SilcMap map, SilcUInt32 width,
+                       SilcInt32 a_x, SilcInt32 a_y,
+                       SilcInt32 b_x, SilcInt32 b_y,
+                       SilcInt16 r, SilcInt16 g, SilcInt16 b)
+{
+  unsigned char p[3] = { r, g, b };
+  int xdiff, ydiff, i;
+  double x, y, slox, sloy;
+
+  SILC_LOG_DEBUG(("draw_line"));
+
+  /* Compute the difference of points */
+  xdiff = b_x - a_x;
+  ydiff = b_y - a_y;
+  if (!xdiff && !ydiff)
+    return FALSE;
+
+  /* Draw the line */
+  if (abs(xdiff) > abs(ydiff)) {
+    sloy = (double)ydiff / (double)xdiff;
+    y = a_y + 0.5;                      /* rounding */
+    if (xdiff > 0) {
+      for (x = a_x; x <= b_x; x++)  {
+       for (i = 0; i < width; i++)
+         silc_map_draw(map, x + i, floor(y), p, 1, 1);
+       y += sloy;
+      }
+    } else {
+      for (x = a_x; x >= b_x; x--)  {
+       for (i = 0; i < width; i++)
+         silc_map_draw(map, x + i, floor(y), p, 1, 1);
+       y -= sloy;
+      }
+    }
+  } else  {
+    slox = (double)xdiff / (double)ydiff;
+    x = a_x + 0.5;                      /* rounding */
+    if (ydiff > 0) {
+      for (y = a_y; y <= b_y; y++)  {
+       for (i = 0; i < width; i++)
+         silc_map_draw(map, floor(x + i), y, p, 1, 1);
+       x += slox;
+      }
+    } else {
+      for (y = a_y; y >= b_y; y--)  {
+       for (i = 0; i < width; i++)
+         silc_map_draw(map, floor(x + i), y, p, 1, 1);
+       x -= slox;
+      }
+    }
+  }
+
+  return TRUE;
+}
+
+/* Print the text string `text' on the bitmap at `x' and `y'.  The color
+   for the text can be provided as argument. */
+
+bool silc_map_draw_text(SilcMap map, const char *text,
+                       SilcInt32 x, SilcInt32 y,
+                       SilcInt16 r, SilcInt16 g, SilcInt16 b)
+{
+  int k, w;
+  int c;
+
+  SILC_LOG_DEBUG(("draw_text"));
+
+  /* Write the text. */
+  w = 0;
+  for (k = 0; k < strlen(text); k++) {
+    c = text[k] - 33;
+    silc_map_draw_raw(map, x + w, y,
+                     map->font.font[c].data,
+                     map->font.font[c].width,
+                     map->font.height, r, g, b);
+    w += map->font.font[c].width;
+  }
+
+  return TRUE;
+}
+
+/* Draw circle on the bitmap map at `x' and `y'.  The center of the
+   circle will be at the `x' and `y'.  If the `label' is provided the
+   text will appear with the circle at `lposx' and `lposy' in relation
+   with the circle. */
+
+bool silc_map_draw_circle(SilcMap map, SilcInt32 x, SilcInt32 y,
+                         SilcInt16 r, SilcInt16 g, SilcInt16 b,
+                         const char *label, SilcInt32 lposx, SilcInt32 lposy,
+                         SilcInt16 lr, SilcInt16 lg, SilcInt16 lb)
+{
+  bool ret;
+
+  SILC_LOG_DEBUG(("draw_circle"));
+
+  y = y - (silc_map_circle.height / 2);
+  x = x - (silc_map_circle.width / 2);
+
+  ret = silc_map_draw_raw(map, x, y,
+                         silc_map_circle.data,
+                         silc_map_circle.width, silc_map_circle.height,
+                         r, g, b);
+  if (!ret)
+    return FALSE;
+
+  if (label)
+    ret = silc_map_draw_text(map, label, x + lposx, y - lposy, lr, lg, lb);
+
+  return ret;
+}
+
+/* Draw rectangle on the bitmap map at `x' and `y'.  The center of the
+   rectangle will be at the `x' and `y'.  If the `label' is provided the
+   text will appear with the circle at `lposx' and `lposy' in relation
+   with the circle. */
+
+bool silc_map_draw_rectangle(SilcMap map, SilcInt32 x, SilcInt32 y,
+                            SilcInt16 r, SilcInt16 g, SilcInt16 b,
+                            const char *label,
+                            SilcInt32 lposx, SilcInt32 lposy,
+                            SilcInt16 lr, SilcInt16 lg, SilcInt16 lb)
+{
+  bool ret;
+
+  SILC_LOG_DEBUG(("draw_rectangle"));
+
+  y = y - (silc_map_rectangle.height / 2);
+  x = x - (silc_map_rectangle.width / 2);
+
+  ret = silc_map_draw_raw(map, x, y,
+                         silc_map_rectangle.data, silc_map_rectangle.width,
+                         silc_map_rectangle.height,
+                         r, g, b);
+  if (!ret)
+    return FALSE;
+
+  if (label)
+    ret = silc_map_draw_text(map, label, x + lposx, y - lposy, lr, lg, lb);
+
+  return ret;
+}
+
+/* Parses the degree position string.  For example, longitude 40 23 10,
+   as in 40 degrees, 23 minutes and 10 seconds east.  Negative degree is to
+   West.  For latitude positive is north and negative south. */
+
+double silc_map_parse_pos(char *pos)
+{
+  double d = 0, m = 0, s = 0;
+  int ret;
+
+  ret = sscanf(pos, "%lf %lf %lf", &d, &m, &s);
+  if (ret < 1) {
+    fprintf(stderr, "Malfromed position string '%s'\n", pos);
+    return 0;
+  }
+
+  return ((d < 0 ? -1 : d > 0 ? 1 : 0) *
+         abs(d) + (m / 60) + (s / 3600));
+}
+
+/* Converts longitude into position in the bitmap */
+
+int silc_map_lon2x(SilcMap map, char *longitude)
+{
+  double meridian, aspmul, lon;
+
+  /* Parse position string */
+  lon = silc_map_parse_pos(longitude);
+
+  /* Compute "aspect ratio multiplier" to get the position in the map. */
+  meridian = (double)map->width / (double)2.0;
+  aspmul = meridian / 180.0;
+
+  /* Compute the position in the bitmap map */
+  return (int)(double)(meridian + (lon * aspmul));
+}
+
+/* Converts latitude into position in the bitmap */
+
+int silc_map_lat2y(SilcMap map, char *latitude)
+{
+  double meridian, aspmul, lat;
+
+  /* Parse position string */
+  lat = silc_map_parse_pos(latitude);
+
+  /* Compute "aspect ratio multiplier" to get the position in the map. */
+  meridian = (double)map->height / (double)2.0;
+  aspmul = meridian / 90.0;
+
+  /* Compute the position in the bitmap map */
+  return (int)(double)(meridian - (lat * aspmul));
+}
+
+/* Parses RGB color string. */
+
+bool silc_map_parse_color(const char *color,
+                         SilcInt16 *r, SilcInt16 *g, SilcInt16 *b)
+{
+  int ret;
+  int rr, gg, bb;
+
+  ret = sscanf(color, "%d %d %d", &rr, &gg, &bb);
+  if (ret < 3) {
+    fprintf(stderr, "Invalid color string: %s\n", color);
+    return FALSE;
+  }
+
+  *r = (SilcInt16)rr;
+  *g = (SilcInt16)gg;
+  *b = (SilcInt16)bb;
+
+  return TRUE;
+}
+
+/* Loads a font file.  The font file format is the following:
+
+   height\n
+   width
+   font data
+   width
+   font data
+   etc.
+
+   If this function is called multiple times the new font replaces the
+   old font. */
+
+bool silc_map_load_font(SilcMap map, const char *filename)
+{
+  FILE *fp;
+  int i, x, y;
+
+  /* Load the file */
+  fp = fopen(filename, "r");
+  if (!fp) {
+    fprintf(stderr, "fopen: %s\n", strerror(errno));
+    return FALSE;
+  }
+
+  /* Read the font height */
+  i = fscanf(fp, "%d\n", &map->font.height);
+  if (i < 1)
+    return FALSE;
+
+  /* Read the font data */
+  for (i = 0; i < 94; i++) {
+    map->font.font[i].width = fgetc(fp);
+
+    for (y = 0; y < map->font.height; y++)
+      for (x = 0; x < map->font.font[i].width; x++)
+       map->font.font[i].data[(y * map->font.font[i].width) + x] = fgetc(fp);
+  }
+
+  return TRUE;
+}
diff --git a/apps/silcmap/silcmap_client.c b/apps/silcmap/silcmap_client.c
new file mode 100644 (file)
index 0000000..03cc47e
--- /dev/null
@@ -0,0 +1,613 @@
+/*
+
+  silcmap_client.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 Pekka Riikonen
+
+  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; version 2 of the License.
+
+  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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "silcmap.h"
+
+/******* Map Client Routines *************************************************/
+
+SILC_TASK_CALLBACK(silc_map_process_done)
+{
+  SilcMap map = context;
+
+  /* Program stops */
+  silc_schedule_stop(map->client->schedule);
+}
+
+/* This function processes the data that was gathered from the server
+   and producess the outputs and the map. */
+
+void silc_map_process_data(SilcMap map, SilcMapConnection mapconn)
+{
+  SilcMapCommand cmd;
+  SilcMap ret_map;
+  SilcInt16 r, g, b, lr, lg, lb;
+
+  map->conn_num++;
+  SILC_LOG_DEBUG(("Processing the data from server (%d/%d)",
+                 map->conn_num, map->conns_num));
+
+  /* Change colors according to server status */
+  silc_map_parse_color(mapconn->up_color, &r, &g, &b);
+  silc_map_parse_color(mapconn->up_text_color, &lr, &lg, &lb);
+  if (mapconn->down) {
+    silc_map_parse_color(mapconn->down_color, &r, &g, &b);
+    silc_map_parse_color(mapconn->down_text_color, &lr, &lg, &lb);
+  }
+
+  /* Execute the map commands */
+  silc_dlist_start(mapconn->commands);
+  while ((cmd = silc_dlist_get(mapconn->commands)) != SILC_LIST_END) {
+    if (cmd->cut) {
+      if (silc_map_cut(map, cmd->x, cmd->y, cmd->width,
+                      cmd->height, &ret_map)) {
+       silc_map_write_ppm(ret_map, cmd->filename);
+       silc_map_free(ret_map);
+      }
+      continue;
+    }
+
+    if (cmd->draw_line) {
+      if (cmd->color_set) {
+       r = cmd->r;
+       g = cmd->g;
+       b = cmd->b;
+      }
+      silc_map_draw_line(map, cmd->width, cmd->x, cmd->y, cmd->x2, cmd->y2,
+                        r, g, b);
+      continue;
+    }
+
+    if (cmd->draw_text) {
+      if (cmd->color_set) {
+       lr = cmd->r;
+       lg = cmd->g;
+       lb = cmd->b;
+      }
+      silc_map_draw_text(map, cmd->text, cmd->x, cmd->y, lr, lg, lb);
+
+      continue;
+    }
+
+    if (cmd->draw_circle) {
+      if (cmd->color_set) {
+       r = cmd->r;
+       g = cmd->g;
+       b = cmd->b;
+      }
+      if (cmd->lcolor_set) {
+       lr = cmd->lr;
+       lg = cmd->lg;
+       lb = cmd->lb;
+      }
+      silc_map_draw_circle(map, cmd->x, cmd->y, r, g, b,
+                          cmd->text, cmd->lposx, cmd->lposy, lr, lg, lb);
+      continue;
+    }
+
+    if (cmd->draw_rectangle) {
+      if (cmd->color_set) {
+       r = cmd->r;
+       g = cmd->g;
+       b = cmd->b;
+      }
+      if (cmd->lcolor_set) {
+       lr = cmd->lr;
+       lg = cmd->lg;
+       lb = cmd->lb;
+      }
+      silc_map_draw_rectangle(map, cmd->x, cmd->y, r, g, b,
+                             cmd->text, cmd->lposx, cmd->lposy, lr, lg, lb);
+      continue;
+    }
+
+  }
+
+  /* Write the html data file */
+  if (map->writehtml.writehtml)
+    silc_map_writehtml(map, mapconn);
+
+  /* If this was last connection, we are done and ready to quit. */
+  if (map->conn_num == map->conns_num) {
+    SILC_LOG_DEBUG(("All connections processed"));
+
+    /* Produce output */
+    if (map->writemap.writemap)
+      silc_map_write_ppm(map, map->writemap.filename);
+    if (map->cut.cut) {
+      if (silc_map_cut(map, map->cut.x, map->cut.y, map->cut.width,
+                      map->cut.height, &ret_map)) {
+       silc_map_write_ppm(ret_map, map->cut.filename);
+       silc_map_free(ret_map);
+      }
+    }
+
+    /* Write the HTML index file */
+    if (map->writehtml.writehtml)
+      silc_map_writehtml_index(map);
+
+    /* Write the HTML map file */
+    if (map->writemaphtml.writemaphtml)
+      silc_map_writemaphtml(map);
+
+    /* Schedule to stop */
+    silc_schedule_task_add(map->client->schedule, 0,
+                          silc_map_process_done, map, 0, 1,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+  }
+}
+
+/* Timeout callback to detect if server is down. */
+
+SILC_TASK_CALLBACK(silc_map_connect_timeout)
+{
+  SilcMapConnection mapconn = context;
+
+  SILC_LOG_DEBUG(("Connection timeout"));
+
+  /* The server is down. */
+  mapconn->down = TRUE;
+
+  /* Continue to produce the data and the map. */
+  silc_map_process_data(mapconn->map, mapconn);
+}
+
+/* Close connection to server */
+
+SILC_TASK_CALLBACK(silc_map_connect_close)
+{
+  SilcMapConnection mapconn = context;
+
+  SILC_LOG_DEBUG(("Closing connection to %s:%d", mapconn->conn->remote_host,
+                 mapconn->conn->remote_port));
+
+  silc_client_close_connection(mapconn->conn->client, mapconn->conn);
+
+  /* Continue to produce the data and the map. */
+  silc_map_process_data(mapconn->map, mapconn);
+}
+
+/* Create connection to remote server to gather information about it. */
+
+void silc_map_connect(SilcMap map, SilcMapConnection mapconn)
+{
+  char *ip;
+
+  if (!mapconn->connect) {
+    silc_schedule_task_add(map->client->schedule, 0,
+                          silc_map_connect_timeout, mapconn, 0, 1,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  /* First configure IP is used to connect. */
+  silc_dlist_start(mapconn->ips);
+  ip = silc_dlist_get(mapconn->ips);
+
+  SILC_LOG_DEBUG(("Creating connection to server %s:%d", ip, mapconn->port));
+
+  /* Create connection.  We'll continue in the silc_connected after
+     connection is created. */
+  silc_client_connect_to_server(map->client, NULL,
+                               mapconn->port, ip, mapconn);
+
+  /* Set connect timeout to detect if the server is down. */
+  silc_schedule_task_add(map->client->schedule, 0,
+                        silc_map_connect_timeout, mapconn,
+                        mapconn->connect_timeout, 0,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}
+
+
+/******* SILC Client Operations **********************************************/
+
+/* "say" client operation is a message from the client library to the
+   application.  It may include error messages or something else.  We
+   just dump them to screen. */
+
+static void
+silc_say(SilcClient client, SilcClientConnection conn,
+        SilcClientMessageType type, char *msg, ...)
+{
+
+}
+
+
+/* Message for a channel. The `sender' is the sender of the message
+   The `channel' is the channel. The `message' is the message.  Note
+   that `message' maybe NULL.  The `flags' indicates message flags
+   and it is used to determine how the message can be interpreted
+   (like it may tell the message is multimedia message). */
+
+static void
+silc_channel_message(SilcClient client, SilcClientConnection conn,
+                    SilcClientEntry sender, SilcChannelEntry channel,
+                    SilcMessagePayload payload,
+                    SilcMessageFlags flags, const unsigned char *message,
+                    SilcUInt32 message_len)
+{
+
+}
+
+
+/* Private message to the client. The `sender' is the sender of the
+   message. The message is `message'and maybe NULL.  The `flags'
+   indicates message flags  and it is used to determine how the message
+   can be interpreted (like it may tell the message is multimedia
+   message). */
+
+static void
+silc_private_message(SilcClient client, SilcClientConnection conn,
+                    SilcClientEntry sender, SilcMessagePayload payload,
+                    SilcMessageFlags flags,
+                    const unsigned char *message,
+                    SilcUInt32 message_len)
+{
+
+}
+
+
+/* Notify message to the client. The notify arguments are sent in the
+   same order as servers sends them. The arguments are same as received
+   from the server except for ID's.  If ID is received application receives
+   the corresponding entry to the ID. For example, if Client ID is received
+   application receives SilcClientEntry.  Also, if the notify type is
+   for channel the channel entry is sent to application (even if server
+   does not send it because client library gets the channel entry from
+   the Channel ID in the packet's header). */
+
+static void
+silc_notify(SilcClient client, SilcClientConnection conn,
+           SilcNotifyType type, ...)
+{
+
+}
+
+
+/* Command handler. This function is called always in the command function.
+   If error occurs it will be called as well. `conn' is the associated
+   client connection. `cmd_context' is the command context that was
+   originally sent to the command. `success' is FALSE if error occurred
+   during command. `command' is the command being processed. It must be
+   noted that this is not reply from server. This is merely called just
+   after application has called the command. Just to tell application
+   that the command really was processed. */
+
+static void
+silc_command(SilcClient client, SilcClientConnection conn,
+            SilcClientCommandContext cmd_context, bool success,
+            SilcCommand command, SilcStatus status)
+{
+
+}
+
+
+/* Command reply handler. This function is called always in the command reply
+   function. If error occurs it will be called as well. Normal scenario
+   is that it will be called after the received command data has been parsed
+   and processed. The function is used to pass the received command data to
+   the application.
+
+   `conn' is the associated client connection. `cmd_payload' is the command
+   payload data received from server and it can be ignored. It is provided
+   if the application would like to re-parse the received command data,
+   however, it must be noted that the data is parsed already by the library
+   thus the payload can be ignored. `success' is FALSE if error occurred.
+   In this case arguments are not sent to the application. The `status' is
+   the command reply status server returned. The `command' is the command
+   reply being processed. The function has variable argument list and each
+   command defines the number and type of arguments it passes to the
+   application (on error they are not sent). */
+
+static void
+silc_command_reply(SilcClient client, SilcClientConnection conn,
+                  SilcCommandPayload cmd_payload, bool success,
+                  SilcCommand command, SilcStatus status, ...)
+{
+  SilcMapConnection mapconn = conn->context;
+  va_list va;
+
+  /* If error occurred in client library with our command, print the error */
+  if (status != SILC_STATUS_OK)
+    fprintf(stderr, "COMMAND REPLY %s: %s\n",
+           silc_get_command_name(command),
+           silc_get_status_message(status));
+
+  if (!success)
+    return;
+
+  va_start(va, status);
+
+  switch (command) {
+  case SILC_COMMAND_STATS:
+    {
+      unsigned char *stats = va_arg(va, unsigned char *);
+      SilcUInt32 stats_len = va_arg(va, SilcUInt32);
+      SilcBufferStruct buf;
+
+      SILC_LOG_DEBUG(("STATS command reply"));
+
+      /* Get statistics structure */
+      silc_buffer_set(&buf, stats, stats_len);
+      silc_buffer_unformat(&buf,
+                          SILC_STR_UI_INT(&mapconn->data.starttime),
+                          SILC_STR_UI_INT(&mapconn->data.uptime),
+                          SILC_STR_UI_INT(&mapconn->data.clients),
+                          SILC_STR_UI_INT(&mapconn->data.channels),
+                          SILC_STR_UI_INT(&mapconn->data.server_ops),
+                          SILC_STR_UI_INT(&mapconn->data.router_ops),
+                          SILC_STR_UI_INT(&mapconn->data.cell_clients),
+                          SILC_STR_UI_INT(&mapconn->data.cell_channels),
+                          SILC_STR_UI_INT(&mapconn->data.cell_servers),
+                          SILC_STR_UI_INT(&mapconn->data.all_clients),
+                          SILC_STR_UI_INT(&mapconn->data.all_channels),
+                          SILC_STR_UI_INT(&mapconn->data.all_servers),
+                          SILC_STR_UI_INT(&mapconn->data.all_routers),
+                          SILC_STR_UI_INT(&mapconn->data.all_server_ops),
+                          SILC_STR_UI_INT(&mapconn->data.all_router_ops),
+                          SILC_STR_END);
+
+      mapconn->stats_received = TRUE;
+    }
+    break;
+
+  case SILC_COMMAND_MOTD:
+    {
+      char *motd = va_arg(va, char *);
+
+      SILC_LOG_DEBUG(("MOTD command reply"));
+
+      mapconn->data.motd = strdup(motd);
+      mapconn->motd_received = TRUE;
+    }
+    break;
+
+  default:
+    break;
+  };
+
+  va_end(va);
+
+  if (mapconn->motd && !mapconn->motd_received)
+    return;
+  if (!mapconn->stats_received)
+    return;
+
+  /* All data is gathered, time to disconnect from the server. */
+  silc_schedule_task_add(client->schedule, 0,
+                        silc_map_connect_close, mapconn, 0, 1,
+                        SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}
+
+
+/* Called to indicate that connection was either successfully established
+   or connecting failed.  This is also the first time application receives
+   the SilcClientConnection objecet which it should save somewhere.
+   If the `success' is FALSE the application must always call the function
+   silc_client_close_connection. */
+
+static void
+silc_connected(SilcClient client, SilcClientConnection conn,
+              SilcClientConnectionStatus status)
+{
+  SilcMapConnection mapconn = conn->context;
+  SilcMap map = mapconn->map;
+
+  silc_schedule_task_del_by_context(client->schedule, mapconn);
+
+  if (status == SILC_CLIENT_CONN_ERROR) {
+    fprintf(stderr, "Could not connect to server\n");
+    silc_client_close_connection(client, conn);
+
+    /* Mark that this server is down. */
+    silc_schedule_task_add(map->client->schedule, 0,
+                          silc_map_connect_timeout, mapconn, 0, 1,
+                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+    return;
+  }
+
+  if (mapconn->down) {
+    /* Already timeouted */
+    silc_client_close_connection(client, conn);
+    return;
+  }
+
+  SILC_LOG_DEBUG(("Connected to server %s:%d", conn->remote_host,
+                 conn->remote_port));
+
+  mapconn->conn = conn;
+
+  /* Get statistics */
+  silc_client_command_call(client, conn, "STATS");
+
+  /* Get motd if requested */
+  if (mapconn->motd) {
+    char motd[256];
+    char *hostname;
+    silc_dlist_start(mapconn->hostnames);
+    hostname = silc_dlist_get(mapconn->hostnames);
+    memset(motd, 0, sizeof(motd));
+    silc_strncat(motd, sizeof(motd), "MOTD ", 5);
+    silc_strncat(motd, sizeof(motd), hostname, strlen(hostname));
+    silc_client_command_call(client, conn, motd);
+  }
+}
+
+
+/* Called to indicate that connection was disconnected to the server.
+   The `status' may tell the reason of the disconnection, and if the
+   `message' is non-NULL it may include the disconnection message
+   received from server. */
+
+static void
+silc_disconnected(SilcClient client, SilcClientConnection conn,
+                 SilcStatus status, const char *message)
+{
+  SilcMapConnection mapconn = conn->context;
+
+  silc_schedule_task_del_by_context(client->schedule, mapconn);
+
+  SILC_LOG_DEBUG(("Disconnected from server %s:%d", conn->remote_host,
+                 conn->remote_port));
+
+  mapconn->conn = NULL;
+}
+
+
+/* Find authentication method and authentication data by hostname and
+   port. The hostname may be IP address as well. When the authentication
+   method has been resolved the `completion' callback with the found
+   authentication method and authentication data is called. The `conn'
+   may be NULL. */
+
+static void
+silc_get_auth_method(SilcClient client, SilcClientConnection conn,
+                    char *hostname, SilcUInt16 port,
+                    SilcGetAuthMeth completion,
+                    void *context)
+{
+  /* No auth */
+  completion(TRUE, SILC_AUTH_NONE, NULL, 0, context);
+}
+
+
+/* Verifies received public key. The `conn_type' indicates which entity
+   (server, client etc.) has sent the public key. If user decides to trust
+   the application may save the key as trusted public key for later
+   use. The `completion' must be called after the public key has been
+   verified. */
+
+static void
+silc_verify_public_key(SilcClient client, SilcClientConnection conn,
+                      SilcSocketType conn_type, unsigned char *pk,
+                      SilcUInt32 pk_len, SilcSKEPKType pk_type,
+                      SilcVerifyPublicKey completion, void *context)
+{
+  /* Accept all keys without verification */
+  completion(TRUE, context);
+}
+
+
+/* Ask (interact, that is) a passphrase from user. The passphrase is
+   returned to the library by calling the `completion' callback with
+   the `context'. The returned passphrase SHOULD be in UTF-8 encoded,
+   if not then the library will attempt to encode. */
+
+static void
+silc_ask_passphrase(SilcClient client, SilcClientConnection conn,
+                   SilcAskPassphrase completion, void *context)
+{
+  completion(NULL, 0, context);
+}
+
+
+/* Notifies application that failure packet was received.  This is called
+   if there is some protocol active in the client.  The `protocol' is the
+   protocol context.  The `failure' is opaque pointer to the failure
+   indication.  Note, that the `failure' is protocol dependant and
+   application must explicitly cast it to correct type.  Usually `failure'
+   is 32 bit failure type (see protocol specs for all protocol failure
+   types). */
+
+static void
+silc_failure(SilcClient client, SilcClientConnection conn,
+            SilcProtocol protocol, void *failure)
+{
+  fprintf(stderr, "Connecting failed (protocol failure)\n");
+}
+
+
+/* Asks whether the user would like to perform the key agreement protocol.
+   This is called after we have received an key agreement packet or an
+   reply to our key agreement packet. This returns TRUE if the user wants
+   the library to perform the key agreement protocol and FALSE if it is not
+   desired (application may start it later by calling the function
+   silc_client_perform_key_agreement). If TRUE is returned also the
+   `completion' and `context' arguments must be set by the application. */
+
+static bool
+silc_key_agreement(SilcClient client, SilcClientConnection conn,
+                  SilcClientEntry client_entry, const char *hostname,
+                  SilcUInt16 port, SilcKeyAgreementCallback *completion,
+                  void **context)
+{
+  return FALSE;
+}
+
+
+/* Notifies application that file transfer protocol session is being
+   requested by the remote client indicated by the `client_entry' from
+   the `hostname' and `port'. The `session_id' is the file transfer
+   session and it can be used to either accept or reject the file
+   transfer request, by calling the silc_client_file_receive or
+   silc_client_file_close, respectively. */
+
+static void
+silc_ftp(SilcClient client, SilcClientConnection conn,
+        SilcClientEntry client_entry, SilcUInt32 session_id,
+        const char *hostname, SilcUInt16 port)
+{
+
+}
+
+
+/* Delivers SILC session detachment data indicated by `detach_data' to the
+   application.  If application has issued SILC_COMMAND_DETACH command
+   the client session in the SILC network is not quit.  The client remains
+   in the network but is detached.  The detachment data may be used later
+   to resume the session in the SILC Network.  The appliation is
+   responsible of saving the `detach_data', to for example in a file.
+
+   The detachment data can be given as argument to the functions
+   silc_client_connect_to_server, or silc_client_add_connection when
+   creating connection to remote server, inside SilcClientConnectionParams
+   structure.  If it is provided the client library will attempt to resume
+   the session in the network.  After the connection is created
+   successfully, the application is responsible of setting the user
+   interface for user into the same state it was before detaching (showing
+   same channels, channel modes, etc).  It can do this by fetching the
+   information (like joined channels) from the client library. */
+
+static void
+silc_detach(SilcClient client, SilcClientConnection conn,
+           const unsigned char *detach_data, SilcUInt32 detach_data_len)
+{
+
+}
+
+/* This structure and all the functions were taken from the
+   lib/silcclient/client_ops_example.c. */
+SilcClientOperations silc_map_client_ops = {
+  silc_say,
+  silc_channel_message,
+  silc_private_message,
+  silc_notify,
+  silc_command,
+  silc_command_reply,
+  silc_connected,
+  silc_disconnected,
+  silc_get_auth_method,
+  silc_verify_public_key,
+  silc_ask_passphrase,
+  silc_failure,
+  silc_key_agreement,
+  silc_ftp,
+  silc_detach
+};
diff --git a/apps/silcmap/silcmap_command.c b/apps/silcmap/silcmap_command.c
new file mode 100644 (file)
index 0000000..9cb8f7b
--- /dev/null
@@ -0,0 +1,933 @@
+/*
+
+  silcmap_command.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 Pekka Riikonen
+
+  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; version 2 of the License.
+
+  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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "silcmap.h"
+
+/******* Command Script Parsing **********************************************/
+
+SILC_CONFIG_CALLBACK(silc_map_cmd_server);
+SILC_CONFIG_CALLBACK(silc_map_cmd_loadmap);
+SILC_CONFIG_CALLBACK(silc_map_cmd_writemap);
+SILC_CONFIG_CALLBACK(silc_map_cmd_writemaphtml);
+SILC_CONFIG_CALLBACK(silc_map_cmd_writehtml);
+SILC_CONFIG_CALLBACK(silc_map_cmd_cut);
+SILC_CONFIG_CALLBACK(silc_map_cmd_rectangle);
+SILC_CONFIG_CALLBACK(silc_map_cmd_circle);
+SILC_CONFIG_CALLBACK(silc_map_cmd_line);
+SILC_CONFIG_CALLBACK(silc_map_cmd_text);
+
+static const SilcConfigTable silc_map_table_loadmap[] =
+{
+  { "filename", SILC_CONFIG_ARG_STR, silc_map_cmd_loadmap, NULL },
+};
+
+static const SilcConfigTable silc_map_table_writemap[] =
+{
+  { "filename", SILC_CONFIG_ARG_STR, silc_map_cmd_writemap, NULL },
+};
+
+static const SilcConfigTable silc_map_table_writemaphtml[] =
+{
+  { "filename", SILC_CONFIG_ARG_STR, silc_map_cmd_writemaphtml, NULL },
+  { "image", SILC_CONFIG_ARG_STR, silc_map_cmd_writemaphtml, NULL },
+  { "cut_lat", SILC_CONFIG_ARG_STRE, silc_map_cmd_writemaphtml, NULL },
+  { "cut_lon", SILC_CONFIG_ARG_STRE, silc_map_cmd_writemaphtml, NULL },
+};
+
+static const SilcConfigTable silc_map_table_writehtml[] =
+{
+  { "filename", SILC_CONFIG_ARG_STR, silc_map_cmd_writehtml, NULL },
+};
+
+static const SilcConfigTable silc_map_table_cut[] =
+{
+  { "lat", SILC_CONFIG_ARG_STR, silc_map_cmd_cut, NULL },
+  { "lon", SILC_CONFIG_ARG_STR, silc_map_cmd_cut, NULL },
+  { "width", SILC_CONFIG_ARG_INT, silc_map_cmd_cut, NULL },
+  { "height", SILC_CONFIG_ARG_INT, silc_map_cmd_cut, NULL },
+  { "filename", SILC_CONFIG_ARG_STR, silc_map_cmd_cut, NULL },
+};
+
+static const SilcConfigTable silc_map_table_rectangle[] =
+{
+  { "lat", SILC_CONFIG_ARG_STR, silc_map_cmd_rectangle, NULL },
+  { "lon", SILC_CONFIG_ARG_STR, silc_map_cmd_rectangle, NULL },
+  { "color", SILC_CONFIG_ARG_STR, silc_map_cmd_rectangle, NULL },
+  { "label", SILC_CONFIG_ARG_STR, silc_map_cmd_rectangle, NULL },
+  { "lposx", SILC_CONFIG_ARG_INT, silc_map_cmd_rectangle, NULL },
+  { "lposy", SILC_CONFIG_ARG_INT, silc_map_cmd_rectangle, NULL },
+  { "lcolor", SILC_CONFIG_ARG_STR, silc_map_cmd_rectangle, NULL },
+};
+
+static const SilcConfigTable silc_map_table_circle[] =
+{
+  { "lat", SILC_CONFIG_ARG_STR, silc_map_cmd_circle, NULL },
+  { "lon", SILC_CONFIG_ARG_STR, silc_map_cmd_circle, NULL },
+  { "color", SILC_CONFIG_ARG_STR, silc_map_cmd_circle, NULL },
+  { "label", SILC_CONFIG_ARG_STR, silc_map_cmd_circle, NULL },
+  { "lposx", SILC_CONFIG_ARG_INT, silc_map_cmd_circle, NULL },
+  { "lposy", SILC_CONFIG_ARG_INT, silc_map_cmd_circle, NULL },
+  { "lcolor", SILC_CONFIG_ARG_STR, silc_map_cmd_circle, NULL },
+};
+
+static const SilcConfigTable silc_map_table_line[] =
+{
+  { "a_lat", SILC_CONFIG_ARG_STR, silc_map_cmd_line, NULL },
+  { "a_lon", SILC_CONFIG_ARG_STR, silc_map_cmd_line, NULL },
+  { "b_lat", SILC_CONFIG_ARG_STR, silc_map_cmd_line, NULL },
+  { "b_lon", SILC_CONFIG_ARG_STR, silc_map_cmd_line, NULL },
+  { "width", SILC_CONFIG_ARG_STR, silc_map_cmd_line, NULL },
+  { "color", SILC_CONFIG_ARG_STR, silc_map_cmd_line, NULL },
+};
+
+static const SilcConfigTable silc_map_table_text[] =
+{
+  { "lat", SILC_CONFIG_ARG_STR, silc_map_cmd_text, NULL },
+  { "lon", SILC_CONFIG_ARG_STR, silc_map_cmd_text, NULL },
+  { "color", SILC_CONFIG_ARG_STR, silc_map_cmd_text, NULL },
+  { "text", SILC_CONFIG_ARG_STR, silc_map_cmd_text, NULL },
+};
+
+static const SilcConfigTable silc_map_table_server[] =
+{
+  /* Details */
+  { "hostname", SILC_CONFIG_ARG_STR, silc_map_cmd_server, NULL },
+  { "ip", SILC_CONFIG_ARG_STR, silc_map_cmd_server, NULL },
+  { "port", SILC_CONFIG_ARG_INT, silc_map_cmd_server, NULL },
+  { "public_key", SILC_CONFIG_ARG_STR, silc_map_cmd_server, NULL },
+  { "country", SILC_CONFIG_ARG_STR, silc_map_cmd_server, NULL },
+  { "city", SILC_CONFIG_ARG_STR, silc_map_cmd_server, NULL },
+  { "admin", SILC_CONFIG_ARG_STR, silc_map_cmd_server, NULL },
+  { "description", SILC_CONFIG_ARG_STRE, silc_map_cmd_server, NULL },
+  { "writemaphtml_url", SILC_CONFIG_ARG_STRE, silc_map_cmd_server, NULL },
+
+  /* Connect params */
+  { "connect", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "connect_timeout", SILC_CONFIG_ARG_INT, silc_map_cmd_server, NULL },
+
+  /* Statistics */
+  { "starttime", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "uptime", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "clients", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "channels", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "server_ops", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "router_ops", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "cell_clients", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "cell_channels", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "cell_servers", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "all_clients", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "all_channels", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "all_servers", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "all_routers", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "all_server_ops", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "all_router_ops", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+  { "motd", SILC_CONFIG_ARG_TOGGLE, silc_map_cmd_server, NULL },
+
+  /* Colors */
+  { "up_color", SILC_CONFIG_ARG_STR, silc_map_cmd_server, NULL },
+  { "down_color", SILC_CONFIG_ARG_STR, silc_map_cmd_server, NULL },
+  { "up_text_color", SILC_CONFIG_ARG_STR, silc_map_cmd_server, NULL },
+  { "down_text_color", SILC_CONFIG_ARG_STR, silc_map_cmd_server, NULL },
+
+  /* Map commands */
+  { "cut", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_cut, silc_map_table_cut },
+  { "rectangle", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_rectangle, silc_map_table_rectangle },
+  { "circle", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_circle, silc_map_table_circle },
+  { "line", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_line, silc_map_table_line },
+  { "text", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_text, silc_map_table_text },
+};
+
+static const SilcConfigTable silc_map_table_main[] =
+{
+  { "server", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_server, silc_map_table_server },
+  { "loadmap", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_loadmap, silc_map_table_loadmap },
+  { "writemap", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_writemap, silc_map_table_writemap },
+  { "writemaphtml", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_writemaphtml, silc_map_table_writemaphtml },
+  { "writehtml", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_writehtml, silc_map_table_writehtml },
+  { "cut", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_cut, silc_map_table_cut },
+  { "rectangle", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_rectangle, silc_map_table_rectangle },
+  { "circle", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_circle, silc_map_table_circle },
+  { "line", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_line, silc_map_table_line },
+  { "text", SILC_CONFIG_ARG_BLOCK,
+    silc_map_cmd_text, silc_map_table_text },
+  { NULL },
+};
+
+/* Command datas.  Used when command is outside server { } section. */
+static char *filename = NULL;
+static char *lat = NULL;
+static char *lon = NULL;
+static char *lat2 = NULL;
+static char *lon2 = NULL;
+static SilcUInt32 width = 0;
+static SilcUInt32 height = 0;
+static SilcInt16 r = 0;
+static SilcInt16 g = 0;
+static SilcInt16 b = 0;
+static SilcInt16 lr = -1;
+static SilcInt16 lg = -1;
+static SilcInt16 lb = -1;
+static char *text = NULL;
+static SilcInt32 lposx = 0;
+static SilcInt32 lposy = 0;
+static bool color_set = FALSE;
+static bool lcolor_set = FALSE;
+
+/* Current server section. */
+SilcMapConnection curr_conn = NULL;
+
+/* Command: server, performs the connection to the remote server and
+   gathers statistical information. */
+
+SILC_CONFIG_CALLBACK(silc_map_cmd_server)
+{
+  SilcMap map = context;
+  int retval = SILC_CONFIG_OK;
+
+  if (!map->bitmap) {
+    fprintf(stderr, "You must call loadmap command before server command\n");
+    return SILC_CONFIG_ESILENT;
+  }
+
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    SILC_LOG_DEBUG(("Server config parsed"));
+
+    if (!curr_conn->ips) {
+      fprintf(stderr, "IP must be configured\n");
+      return SILC_CONFIG_EMISSFIELDS;
+    }
+    if (!curr_conn->hostnames) {
+      fprintf(stderr, "Hostname must be configured\n");
+      return SILC_CONFIG_EMISSFIELDS;
+    }
+    if (!curr_conn->port) {
+      fprintf(stderr, "Port must be configured\n");
+      return SILC_CONFIG_EMISSFIELDS;
+    }
+
+    /* The server data is now gathered.  We continue by creating the
+       connection to the server and executing the requested commands. */
+    silc_map_connect(map, curr_conn);
+    map->conns_num++;
+
+    /* Cleanup */
+    curr_conn = NULL;
+
+    return retval;
+  }
+
+  /* Mark the current parsed server */
+  if (!curr_conn) {
+    curr_conn = silc_calloc(1, sizeof(*curr_conn));
+    if (!curr_conn)
+      return SILC_CONFIG_ESILENT;
+
+    curr_conn->hostnames = silc_dlist_init();
+    curr_conn->ips = silc_dlist_init();
+    curr_conn->commands = silc_dlist_init();
+    curr_conn->map = map;
+    if (!map->conns)
+      map->conns = silc_dlist_init();
+    silc_dlist_add(map->conns, curr_conn);
+  }
+
+  if (!strcmp(name, "hostname")) {
+    silc_dlist_add(curr_conn->hostnames, strdup((char *)val));
+  } else if (!strcmp(name, "ip")) {
+    silc_dlist_add(curr_conn->ips, strdup((char *)val));
+  } else if (!strcmp(name, "port")) {
+    curr_conn->port = (SilcUInt32)*(int *)val;
+  } else if (!strcmp(name, "public_key")) {
+    curr_conn->public_key = strdup((char *)val);
+  } else if (!strcmp(name, "country")) {
+    curr_conn->country = strdup((char *)val);
+  } else if (!strcmp(name, "city")) {
+    curr_conn->city = strdup((char *)val);
+  } else if (!strcmp(name, "admin")) {
+    curr_conn->admin = strdup((char *)val);
+  } else if (!strcmp(name, "description")) {
+    curr_conn->description = strdup((char *)val);
+  } else if (!strcmp(name, "writemaphtml_url")) {
+    curr_conn->writemaphtml_url = strdup((char *)val);
+  } else if (!strcmp(name, "connect")) {
+    curr_conn->connect = (bool)*(int *)val;
+  } else if (!strcmp(name, "connect_timeout")) {
+    curr_conn->connect_timeout = (SilcUInt32)*(int *)val;
+  } else if (!strcmp(name, "starttime")) {
+    curr_conn->starttime = (bool)*(int *)val;
+  } else if (!strcmp(name, "uptime")) {
+    curr_conn->uptime = (bool)*(int *)val;
+  } else if (!strcmp(name, "clients")) {
+    curr_conn->clients = (bool)*(int *)val;
+  } else if (!strcmp(name, "channels")) {
+    curr_conn->channels = (bool)*(int *)val;
+  } else if (!strcmp(name, "server_ops")) {
+    curr_conn->server_ops = (bool)*(int *)val;
+  } else if (!strcmp(name, "router_ops")) {
+    curr_conn->router_ops = (bool)*(int *)val;
+  } else if (!strcmp(name, "cell_clients")) {
+    curr_conn->cell_clients = (bool)*(int *)val;
+  } else if (!strcmp(name, "cell_channels")) {
+    curr_conn->cell_channels = (bool)*(int *)val;
+  } else if (!strcmp(name, "cell_servers")) {
+    curr_conn->cell_servers = (bool)*(int *)val;
+  } else if (!strcmp(name, "all_clients")) {
+    curr_conn->all_clients = (bool)*(int *)val;
+  } else if (!strcmp(name, "all_channels")) {
+    curr_conn->all_channels = (bool)*(int *)val;
+  } else if (!strcmp(name, "all_servers")) {
+    curr_conn->all_servers = (bool)*(int *)val;
+  } else if (!strcmp(name, "all_routers")) {
+    curr_conn->all_routers = (bool)*(int *)val;
+  } else if (!strcmp(name, "all_server_ops")) {
+    curr_conn->all_server_ops = (bool)*(int *)val;
+  } else if (!strcmp(name, "all_router_ops")) {
+    curr_conn->all_router_ops = (bool)*(int *)val;
+  } else if (!strcmp(name, "motd")) {
+    curr_conn->motd = (bool)*(int *)val;
+  } else if (!strcmp(name, "up_color")) {
+    curr_conn->up_color = strdup((char *)val);
+  } else if (!strcmp(name, "down_color")) {
+    curr_conn->down_color = strdup((char *)val);
+  } else if (!strcmp(name, "up_text_color")) {
+    curr_conn->up_text_color = strdup((char *)val);
+  } else if (!strcmp(name, "down_text_color")) {
+    curr_conn->down_text_color = strdup((char *)val);
+  } else {
+    retval = SILC_CONFIG_ESILENT;
+  }
+
+  return retval;
+}
+
+/* Command: loadmap, loadmaps the bitmap map image. */
+
+SILC_CONFIG_CALLBACK(silc_map_cmd_loadmap)
+{
+  SilcMap map = context;
+  int retval = SILC_CONFIG_OK;
+
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    if (!filename)
+      return SILC_CONFIG_EMISSFIELDS;
+
+    SILC_LOG_DEBUG(("loadmap: file: %s", filename));
+
+    /* Destroy old bitmap if loadmaped */
+    silc_free(map->bitmap);
+
+    /* Loadmap the bitmap image */
+    if (!silc_map_load_ppm(map, filename))
+      retval = SILC_CONFIG_ESILENT;
+
+    /* Cleanup */
+    silc_free(filename);
+    filename = NULL;
+
+    return retval;
+  }
+
+  if (!strcmp(name, "filename"))
+    filename = strdup((char *)val);
+  else
+    retval = SILC_CONFIG_ESILENT;
+
+  return retval;
+}
+
+/* Command: writemap, writemap the map into bitmap file. */
+
+SILC_CONFIG_CALLBACK(silc_map_cmd_writemap)
+{
+  SilcMap map = context;
+  int retval = SILC_CONFIG_OK;
+
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    if (!filename)
+      return SILC_CONFIG_EMISSFIELDS;
+
+    SILC_LOG_DEBUG(("writemap: file: %s", filename));
+
+    /* Execute directly if there are no connections */
+    if (map->conns_num == 0) {
+      /* Writemap the map */
+      if (!silc_map_write_ppm(map, filename))
+       retval = SILC_CONFIG_ESILENT;
+    } else {
+      map->writemap.filename = strdup(filename);
+      map->writemap.writemap = TRUE;
+    }
+
+    /* Cleanup */
+    silc_free(filename);
+    filename = NULL;
+
+    return retval;
+  }
+
+  if (!strcmp(name, "filename"))
+    filename = strdup((char *)val);
+  else
+    retval = SILC_CONFIG_ESILENT;
+
+  return retval;
+}
+
+/* Command: writemaphtml, writes HTML map of the image map. */
+
+SILC_CONFIG_CALLBACK(silc_map_cmd_writemaphtml)
+{
+  SilcMap map = context;
+  int retval = SILC_CONFIG_OK;
+
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    if (!filename)
+      return SILC_CONFIG_EMISSFIELDS;
+
+    SILC_LOG_DEBUG(("writehmaphtml: file: %s", filename));
+
+    /* Will generate HTML map page */
+    map->writemaphtml.filename = filename;
+    map->writemaphtml.text = text;
+    if (lon)
+      map->writemaphtml.x = silc_map_lon2x(map, lon);
+    if (lat)
+      map->writemaphtml.y = silc_map_lat2y(map, lat);
+    map->writemaphtml.writemaphtml = TRUE;
+
+    /* Clean up */
+    silc_free(lat);
+    silc_free(lon);
+    filename = NULL;
+    text = NULL;
+    lat = lon = NULL;
+
+    return retval;
+  }
+
+  if (!strcmp(name, "filename"))
+    filename = strdup((char *)val);
+  else if (!strcmp(name, "image"))
+    text = strdup((char *)val);
+  else if (!strcmp(name, "cut_lat"))
+    lat = strdup((char *)val);
+  else if (!strcmp(name, "cut_lon"))
+    lon = strdup((char *)val);
+  else
+    retval = SILC_CONFIG_ESILENT;
+
+  return retval;
+}
+
+/* Command: writehtml, writes the gathered data into HTML pages. */
+
+SILC_CONFIG_CALLBACK(silc_map_cmd_writehtml)
+{
+  SilcMap map = context;
+  int retval = SILC_CONFIG_OK;
+
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    if (!filename)
+      return SILC_CONFIG_EMISSFIELDS;
+
+    SILC_LOG_DEBUG(("writehtml: file: %s", filename));
+
+    /* Will generate HTML pages */
+    map->writehtml.filename = filename;
+    map->writehtml.writehtml = TRUE;
+    filename = NULL;
+
+    return retval;
+  }
+
+  if (!strcmp(name, "filename"))
+    filename = strdup((char *)val);
+  else
+    retval = SILC_CONFIG_ESILENT;
+
+  return retval;
+}
+
+/* Command: cut, cut's a specified area from the map. */
+
+SILC_CONFIG_CALLBACK(silc_map_cmd_cut)
+{
+  SilcMap map = context;
+  int retval = SILC_CONFIG_OK;
+  bool ret;
+
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    SilcMap map2;
+
+    if (!filename || !lat || !lon || !width || !height)
+      return SILC_CONFIG_EMISSFIELDS;
+
+    SILC_LOG_DEBUG(("cut: lat: %s lon: %s w: %ld h: %ld file: %s",
+                   lat, lon, width, height, filename));
+
+    /* Execute directly if not inside connection block */
+    if (!curr_conn) {
+      if (!map->conns_num) {
+       /* Before any connection blocks */
+
+       /* Cut the chunk from the map. */
+       ret = silc_map_cut(map, silc_map_lon2x(map, lon),
+                          silc_map_lat2y(map, lat),
+                          width, height, &map2);
+       if (ret) {
+         /* Writemap the chunk. */
+         ret = silc_map_write_ppm(map2, filename);
+         silc_map_free(map2);
+       }
+       if (!ret)
+         retval = SILC_CONFIG_ESILENT;
+      } else {
+       /* After all connection blocks */
+       map->cut.filename = strdup(filename);
+       map->cut.x = silc_map_lon2x(map, lon);
+       map->cut.y = silc_map_lat2y(map, lat);
+       map->cut.width = width;
+       map->cut.height = height;
+       map->cut.cut = TRUE;
+      }
+    } else {
+      SilcMapCommand cmd = silc_calloc(1, sizeof(*cmd));
+      if (!cmd)
+       return SILC_CONFIG_ESILENT;
+
+      silc_dlist_add(curr_conn->commands, cmd);
+      cmd->filename = strdup(filename);
+      cmd->x = silc_map_lon2x(map, lon);
+      cmd->y = silc_map_lat2y(map, lat);
+      cmd->width = width;
+      cmd->height = height;
+      cmd->cut = TRUE;
+    }
+
+    /* Cleanup */
+    silc_free(filename);
+    silc_free(lat);
+    silc_free(lon);
+    filename = NULL;
+    lat = NULL;
+    lon = NULL;
+    width = 0;
+    height = 0;
+
+    return retval;
+  }
+
+  if (!strcmp(name, "lat"))
+    lat = strdup((char *)val);
+  else if (!strcmp(name, "lon"))
+    lon = strdup((char *)val);
+  else if (!strcmp(name, "width"))
+    width = (SilcUInt32)*(int *)val;
+  else if (!strcmp(name, "height"))
+    height = (SilcUInt32)*(int *)val;
+  else if (!strcmp(name, "filename"))
+    filename = strdup((char *)val);
+  else
+    retval = SILC_CONFIG_ESILENT;
+
+  return retval;
+}
+
+/* Command: rectangle, draws a rectangle on the map. */
+
+SILC_CONFIG_CALLBACK(silc_map_cmd_rectangle)
+{
+  SilcMap map = context;
+  int retval = SILC_CONFIG_OK;
+  bool ret;
+
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    if (!lat || !lon)
+      return SILC_CONFIG_EMISSFIELDS;
+
+    SILC_LOG_DEBUG(("rectangle: lat: %s lon: %s color: %d %d %d",
+                   lat, lon, r, g, b));
+
+    if (lr == -1) {
+      lr = r;
+      lg = g;
+      lb = b;
+    }
+
+    /* Execute directly if not for connection */
+    if (!curr_conn) {
+      /* Draw the rectangle */
+      ret = silc_map_draw_rectangle(map, silc_map_lon2x(map, lon),
+                                   silc_map_lat2y(map, lat),
+                                   r, g, b, text, lposx, lposy, lr, lg, lb);
+      if (!ret)
+       retval = SILC_CONFIG_ESILENT;
+    } else {
+      SilcMapCommand cmd = silc_calloc(1, sizeof(*cmd));
+      if (!cmd)
+       return SILC_CONFIG_ESILENT;
+
+      silc_dlist_add(curr_conn->commands, cmd);
+      cmd->r = r;
+      cmd->g = g;
+      cmd->b = b;
+      cmd->lr = lr;
+      cmd->lg = lg;
+      cmd->lb = lb;
+      cmd->x = silc_map_lon2x(map, lon);
+      cmd->y = silc_map_lat2y(map, lat);
+      cmd->text = strdup(text);
+      cmd->lposx = lposx;
+      cmd->lposy = lposy;
+      cmd->draw_rectangle = TRUE;
+      cmd->color_set = color_set;
+      cmd->lcolor_set = lcolor_set;
+    }
+
+    /* Cleanup */
+    silc_free(text);
+    silc_free(lat);
+    silc_free(lon);
+    text = NULL;
+    lat = NULL;
+    lon = NULL;
+    lposx = 0;
+    lposy = 0;
+    lr = lg = lb = -1;
+    color_set = lcolor_set = FALSE;
+
+    return retval;
+  }
+
+  if (!strcmp(name, "lat"))
+    lat = strdup((char *)val);
+  else if (!strcmp(name, "lon"))
+    lon = strdup((char *)val);
+  else if (!strcmp(name, "color")) {
+    if (!silc_map_parse_color((const char *)val, &r, &g, &b))
+      retval = SILC_CONFIG_ESILENT;
+    color_set = TRUE;
+  } else if (!strcmp(name, "label"))
+    text = strdup((char *)val);
+  else if (!strcmp(name, "lposx"))
+    lposx = (SilcInt32)*(int *)val;
+  else if (!strcmp(name, "lposy"))
+    lposy = (SilcInt32)*(int *)val;
+  else if (!strcmp(name, "lcolor")) {
+    if (!silc_map_parse_color((const char *)val, &lr, &lg, &lb))
+      retval = SILC_CONFIG_ESILENT;
+    lcolor_set = TRUE;
+ } else
+    retval = SILC_CONFIG_ESILENT;
+
+  return retval;
+}
+
+/* Command: circle, draws a circle on the map. */
+
+SILC_CONFIG_CALLBACK(silc_map_cmd_circle)
+{
+  SilcMap map = context;
+  int retval = SILC_CONFIG_OK;
+  bool ret;
+
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    if (!lat || !lon)
+      return SILC_CONFIG_EMISSFIELDS;
+
+    SILC_LOG_DEBUG(("circle: lat: %s lon: %s color: %d %d %d",
+                   lat, lon, r, g, b));
+
+    if (lr == -1) {
+      lr = r;
+      lg = g;
+      lb = b;
+    }
+
+    /* Execute directly if not for connection */
+    if (!curr_conn) {
+      /* Draw the circle */
+      ret = silc_map_draw_circle(map, silc_map_lon2x(map, lon),
+                                silc_map_lat2y(map, lat),
+                                r, g, b, text, lposx, lposy, lr, lg, lb);
+      if (!ret)
+       retval = SILC_CONFIG_ESILENT;
+    } else {
+      SilcMapCommand cmd = silc_calloc(1, sizeof(*cmd));
+      if (!cmd)
+       return SILC_CONFIG_ESILENT;
+
+      silc_dlist_add(curr_conn->commands, cmd);
+      cmd->r = r;
+      cmd->g = g;
+      cmd->b = b;
+      cmd->lr = lr;
+      cmd->lg = lg;
+      cmd->lb = lb;
+      cmd->x = silc_map_lon2x(map, lon);
+      cmd->y = silc_map_lat2y(map, lat);
+      cmd->text = strdup(text);
+      cmd->lposx = lposx;
+      cmd->lposy = lposy;
+      cmd->draw_circle = TRUE;
+      cmd->color_set = color_set;
+      cmd->lcolor_set = lcolor_set;
+    }
+
+    /* Cleanup */
+    silc_free(text);
+    silc_free(lat);
+    silc_free(lon);
+    text = NULL;
+    lat = NULL;
+    lon = NULL;
+    lposx = 0;
+    lposy = 0;
+    lr = lg = lb = -1;
+    color_set = lcolor_set = FALSE;
+
+    return retval;
+  }
+
+  if (!strcmp(name, "lat"))
+    lat = strdup((char *)val);
+  else if (!strcmp(name, "lon"))
+    lon = strdup((char *)val);
+  else if (!strcmp(name, "color")) {
+    if (!silc_map_parse_color((const char *)val, &r, &g, &b))
+      retval = SILC_CONFIG_ESILENT;
+    color_set = TRUE;
+  } else if (!strcmp(name, "label"))
+    text = strdup((char *)val);
+  else if (!strcmp(name, "lposx"))
+    lposx = (SilcInt32)*(int *)val;
+  else if (!strcmp(name, "lposy"))
+    lposy = (SilcInt32)*(int *)val;
+  else if (!strcmp(name, "lcolor")) {
+    if (!silc_map_parse_color((const char *)val, &lr, &lg, &lb))
+      retval = SILC_CONFIG_ESILENT;
+    lcolor_set = TRUE;
+  } else
+    retval = SILC_CONFIG_ESILENT;
+
+  return retval;
+}
+
+/* Command: line, draws a line between two points in the map. */
+
+SILC_CONFIG_CALLBACK(silc_map_cmd_line)
+{
+  SilcMap map = context;
+  int retval = SILC_CONFIG_OK;
+  bool ret;
+
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    if (!lat || !lon || !lat2 || !lon2)
+      return SILC_CONFIG_EMISSFIELDS;
+
+    SILC_LOG_DEBUG(("line: alat: %s alon: %s blat: %s blon: %s "
+                   "width: %ld color: %d %d %d",
+                   lat, lon, lat2, lon2, width, r, g, b));
+
+    if (!width)
+      width = 1;
+
+    /* Execute directly if not for connection */
+    if (!curr_conn) {
+      /* Draw the line */
+      ret = silc_map_draw_line(map, width,
+                              silc_map_lon2x(map, lon),
+                              silc_map_lat2y(map, lat),
+                              silc_map_lon2x(map, lon2),
+                              silc_map_lat2y(map, lat2),
+                              r, g, b);
+      if (!ret)
+       retval = SILC_CONFIG_ESILENT;
+    } else {
+      SilcMapCommand cmd = silc_calloc(1, sizeof(*cmd));
+      if (!cmd)
+       return SILC_CONFIG_ESILENT;
+
+      silc_dlist_add(curr_conn->commands, cmd);
+      cmd->r = r;
+      cmd->g = g;
+      cmd->b = b;
+      cmd->x = silc_map_lon2x(map, lon);
+      cmd->y = silc_map_lat2y(map, lat);
+      cmd->x2 = silc_map_lon2x(map, lon2);
+      cmd->y2 = silc_map_lat2y(map, lat2);
+      cmd->width = width;
+      cmd->draw_line = TRUE;
+      cmd->color_set = color_set;
+    }
+
+    /* Cleanup */
+    silc_free(lat);
+    silc_free(lon);
+    silc_free(lat2);
+    silc_free(lon2);
+    lat = NULL;
+    lon = NULL;
+    lat2 = NULL;
+    lon2 = NULL;
+    width = 0;
+    color_set = FALSE;
+
+    return retval;
+  }
+
+  if (!strcmp(name, "a_lat"))
+    lat = strdup((char *)val);
+  else if (!strcmp(name, "a_lon"))
+    lon = strdup((char *)val);
+  else if (!strcmp(name, "b_lat"))
+    lat2 = strdup((char *)val);
+  else if (!strcmp(name, "b_lon"))
+    lon2 = strdup((char *)val);
+  else if (!strcmp(name, "width"))
+    width = (SilcUInt32)*(int *)val;
+  else if (!strcmp(name, "color")) {
+    if (!silc_map_parse_color((const char *)val, &r, &g, &b))
+      retval = SILC_CONFIG_ESILENT;
+    color_set = TRUE;
+  } else
+    retval = SILC_CONFIG_ESILENT;
+
+  return retval;
+}
+
+/* Command: text, prints a text on the map. */
+
+SILC_CONFIG_CALLBACK(silc_map_cmd_text)
+{
+  SilcMap map = context;
+  int retval = SILC_CONFIG_OK;
+  bool ret;
+
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    if (!lat || !lon || !text)
+      return SILC_CONFIG_EMISSFIELDS;
+
+    SILC_LOG_DEBUG(("text: lat: %s lon: %s color: %d %d %d text: %s",
+                   lat, lon, r, g, b, text));
+
+    /* Execute directly if not for connection */
+    if (!curr_conn) {
+      /* Print the text */
+      ret = silc_map_draw_text(map, text,
+                              silc_map_lon2x(map, lon),
+                              silc_map_lat2y(map, lat),
+                              r, g, b);
+      if (!ret)
+       retval = SILC_CONFIG_ESILENT;
+    } else {
+      SilcMapCommand cmd = silc_calloc(1, sizeof(*cmd));
+      if (!cmd)
+       return SILC_CONFIG_ESILENT;
+
+      silc_dlist_add(curr_conn->commands, cmd);
+      cmd->r = r;
+      cmd->g = g;
+      cmd->b = b;
+      cmd->x = silc_map_lon2x(map, lon);
+      cmd->y = silc_map_lat2y(map, lat);
+      cmd->text = strdup(text);
+      cmd->draw_text = TRUE;
+      cmd->color_set = color_set;
+    }
+
+    /* Cleanup */
+    silc_free(text);
+    silc_free(lat);
+    silc_free(lon);
+    text = NULL;
+    lat = NULL;
+    lon = NULL;
+    color_set = FALSE;
+
+    return retval;
+  }
+
+  if (!strcmp(name, "lat"))
+    lat = strdup((char *)val);
+  else if (!strcmp(name, "lon"))
+    lon = strdup((char *)val);
+  else if (!strcmp(name, "color")) {
+    if (!silc_map_parse_color((const char *)val, &r, &g, &b))
+      retval = SILC_CONFIG_ESILENT;
+    color_set = TRUE;
+  } else if (!strcmp(name, "text"))
+    text = strdup((char *)val);
+  else
+    retval = SILC_CONFIG_ESILENT;
+
+  return retval;
+}
+
+/* Parses the commands from the file `filename'. */
+
+bool silc_map_commands_parse(SilcMap map, const char *filename)
+{
+  SilcConfigEntity ent;
+  SilcConfigFile *file;
+  bool retval = TRUE;
+  int ret;
+
+  SILC_LOG_DEBUG(("Parsing commands"));
+
+  /* Open commands file */
+  file = silc_config_open(filename);
+  if (!file) {
+    fprintf(stderr, "Cannot open commands file '%s'\n", filename);
+    return FALSE;
+  }
+
+  /* Parse the commands */
+  ent = silc_config_init(file);
+  silc_config_register_table(ent, silc_map_table_main, map);
+  ret = silc_config_main(ent);
+
+  SILC_LOG_DEBUG(("Parsing status: %s", silc_config_strerror(ret)));
+
+  if (ret && ret != SILC_CONFIG_ESILENT) {
+    fprintf(stderr, "Error parsing commands: %s, line %ld\n",
+           silc_config_strerror(ret), silc_config_get_line(file));
+    retval = FALSE;
+  }
+
+  /* Cleanup */
+  silc_config_close(file);
+  return retval;
+}
diff --git a/apps/silcmap/silcmap_html.c b/apps/silcmap/silcmap_html.c
new file mode 100644 (file)
index 0000000..a8a8183
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+
+  silcmap_html.c
+
+  Author: Pekka Riikonen <priikone@silcnet.org>
+
+  Copyright (C) 2003 Pekka Riikonen
+
+  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; version 2 of the License.
+
+  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.
+
+*/
+
+#include "silcincludes.h"
+#include "silcclient.h"
+#include "silcmap.h"
+
+/* Write the HTML data file of the gathered data from the connection. */
+
+bool silc_map_writehtml(SilcMap map, SilcMapConnection mapconn)
+{
+  FILE *fp;
+  char *hostname;
+  char filename[256];
+
+  /* Generate data filename.  First configure hostname is the filename */
+  silc_dlist_start(mapconn->hostnames);
+  hostname = silc_dlist_get(mapconn->hostnames);
+  memset(filename, 0, sizeof(filename));
+  snprintf(filename, sizeof(filename) - 1, "%s_%d.html", hostname,
+          mapconn->port);
+
+  /* Open for writing */
+  fp = fopen(filename, "w+");
+  if (!fp) {
+    fprintf(stderr, "Could not open file '%s'\n", filename);
+    return FALSE;
+  }
+
+  /* Write the HTML page */
+
+  fprintf(fp, "<!-- Automatically generated by silcmap --!>\n");
+  fprintf(fp, "<br /><hr ><br />\n");
+
+  /* General stuff */
+
+  fprintf(fp, "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n");
+  silc_dlist_start(mapconn->hostnames);
+  while ((hostname = silc_dlist_get(mapconn->hostnames)) != SILC_LIST_END)
+    fprintf(fp, "<tr><td><b>Hostname</b></td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", hostname);
+
+  silc_dlist_start(mapconn->ips);
+  while ((hostname = silc_dlist_get(mapconn->ips)) != SILC_LIST_END)
+    fprintf(fp, "<tr><td><b>IP</b></td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", hostname);
+
+  fprintf(fp, "<tr><td><b>Port</b></td><td>&nbsp;:</td><td>&nbsp;%d</td></tr>\n", mapconn->port);
+  fprintf(fp, "<tr><td><b>Country</b></td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", mapconn->country);
+  fprintf(fp, "<tr><td><b>City</b></td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", mapconn->city);
+  fprintf(fp, "<tr><td><b>Admin</b></td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", mapconn->admin);
+  fprintf(fp, "</table>\n");
+
+  /* Public key */
+  if (mapconn->public_key) {
+    SilcPublicKey public_key;
+    SilcPublicKeyIdentifier ident;
+    char *fingerprint, *babbleprint;
+    unsigned char *pk;
+    SilcUInt32 pk_len;
+    SilcPKCS pkcs;
+    SilcUInt32 key_len = 0;
+    FILE *pd;
+    unsigned char *pdd;
+
+    fprintf(fp, "&nbsp;<br /><hr ><br />\n");
+    fprintf(fp, "<b>Public Key:</b>&nbsp;<br />\n");
+    fprintf(fp, "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n");
+
+    if (silc_pkcs_load_public_key(mapconn->public_key, &public_key,
+                                 SILC_PKCS_FILE_PEM) == FALSE)
+      if (silc_pkcs_load_public_key(mapconn->public_key, &public_key,
+                                   SILC_PKCS_FILE_BIN) == FALSE) {
+       fprintf(stderr, "Could not load public key file `%s'\n",
+               mapconn->public_key);
+       return FALSE;
+      }
+
+    ident = silc_pkcs_decode_identifier(public_key->identifier);
+    pk = silc_pkcs_public_key_encode(public_key, &pk_len);
+    fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
+    babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
+
+    if (silc_pkcs_alloc(public_key->name, &pkcs)) {
+      key_len = silc_pkcs_public_key_set(pkcs, public_key);
+      silc_pkcs_free(pkcs);
+    }
+
+    fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+    fprintf(fp, "Public key file</td><td>&nbsp;:</td><td>&nbsp;<a href=\"%s\">%s</a></td></tr>\n",
+           mapconn->public_key, mapconn->public_key);
+    fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+    fprintf(fp, "Algorithm</td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", public_key->name);
+    if (key_len) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Key length (bits)</td><td>&nbsp;:</td><td>&nbsp;%d</td></tr>\n", (unsigned int)key_len);
+    }
+    if (ident->realname) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Real name</td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", ident->realname);
+    }
+    if (ident->username) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Username</td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", ident->username);
+    }
+    if (ident->host) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Hostname</td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", ident->host);
+    }
+    if (ident->email) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Email</td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", ident->email);
+    }
+    if (ident->org) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Organization</td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", ident->org);
+    }
+    if (ident->country) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Country</td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", ident->country);
+    }
+    fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+    fprintf(fp, "Fingerprint (SHA1)</td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", fingerprint);
+    fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+    fprintf(fp, "Babbleprint (SHA1)</td><td>&nbsp;:</td><td>&nbsp;%s</td></tr>\n", babbleprint);
+    fprintf(fp, "</table>\n");
+
+    pd = fopen(mapconn->public_key, "r");
+    if (!pd)
+      return FALSE;
+
+    pk_len = silc_file_size(mapconn->public_key);
+    pdd = silc_calloc(pk_len + 1, sizeof(*pdd));
+    if (!pdd)
+      return FALSE;
+    fread(pdd, pk_len, 1, pd);
+
+    fprintf(fp, "<pre><tt>\n");
+    fprintf(fp, "%s", pdd);
+    fprintf(fp, "</tt></pre>\n");
+
+    fclose(pd);
+    silc_free(pdd);
+    silc_free(fingerprint);
+    silc_free(babbleprint);
+    silc_free(pk);
+    silc_pkcs_public_key_free(public_key);
+    silc_pkcs_free_identifier(ident);
+  }
+
+  /* Description */
+  if (mapconn->description && mapconn->description[0]) {
+    fprintf(fp, "<hr ><br />\n");
+    fprintf(fp, "<b>Description:</b>&nbsp;<br />\n");
+    fprintf(fp, "%s<br />&nbsp;<br />\n", mapconn->description);
+  }
+
+  /* Status */
+  if (mapconn->connect) {
+    fprintf(fp, "<hr ><br />\n");
+    fprintf(fp, "<b>Server status:</b>&nbsp;<br />\n");
+    if (mapconn->down)
+      fprintf(fp,
+             "Server is currently down and unreachable. "
+             "Please try again later to connect the server.<br />\n");
+    else
+      fprintf(fp,
+             "Server is up and running<br />\n");
+  }
+
+  if (mapconn->connect && !mapconn->down) {
+    int days, hours, mins, secs, uptime;
+
+    uptime = mapconn->data.uptime;
+    days = uptime / (24 * 60 * 60);
+    uptime -= days * (24 * 60 * 60);
+    hours = uptime / (60 * 60);
+    uptime -= hours * (60 * 60);
+    mins = uptime / 60;
+    uptime -= mins * 60;
+    secs = uptime;
+
+    /* Statistics */
+    fprintf(fp, "<br />\n");
+    fprintf(fp, "<b>Server statistics:</b>&nbsp;<br />\n");
+    fprintf(fp, "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n");
+    if (mapconn->starttime) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Server start time</td><td>&nbsp;:</td><td>&nbsp;%s<td></tr>\n",
+             silc_get_time(mapconn->data.starttime));
+    }
+    if (mapconn->uptime) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Server uptime</td><td>&nbsp;:</td><td>&nbsp;%d days %d hours %d mins %d secs<td></tr>\n",
+             days, hours, mins, secs);
+    }
+    if (mapconn->clients) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Local clients</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.clients);
+    }
+    if (mapconn->channels) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Local channels</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.channels);
+    }
+    if (mapconn->server_ops) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Local server operators</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.server_ops);
+    }
+    if (mapconn->router_ops) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Local router operators</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.router_ops);
+    }
+    if (mapconn->cell_clients) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Cell clients</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.cell_clients);
+    }
+    if (mapconn->cell_channels) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Cell channels</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.cell_channels);
+    }
+    if (mapconn->cell_servers) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "Cell servers</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.cell_servers);
+    }
+    if (mapconn->all_clients) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "All SILC clients</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.all_clients);
+    }
+    if (mapconn->all_channels) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "All SILC channels</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.all_channels);
+    }
+    if (mapconn->all_servers) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "All SILC servers</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.all_servers);
+    }
+    if (mapconn->all_routers) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "All SILC routers</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.all_routers);
+    }
+    if (mapconn->all_server_ops) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "All SILC server operators</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.all_server_ops);
+    }
+    if (mapconn->all_router_ops) {
+      fprintf(fp, "<tr><td>&nbsp;&nbsp;");
+      fprintf(fp, "All SILC router operators</td><td>&nbsp;:</td><td>&nbsp;%ld</td></tr>\n",
+             mapconn->data.all_router_ops);
+    }
+    fprintf(fp, "</table>\n");
+  }
+
+  /* motd */
+  if (mapconn->motd) {
+    fprintf(fp, "&nbsp;<br /><hr ><br />\n");
+    fprintf(fp, "<b>Message of the Day:</b>&nbsp;<br />\n");
+    fprintf(fp, "<pre><tt>%s</tt><pre>\n", mapconn->data.motd);
+  }
+
+  fprintf(fp, "<br />\n");
+
+  fclose(fp);
+  return TRUE;
+}
+
+/* Write the HTML index file that lists all servers. */
+
+bool silc_map_writehtml_index(SilcMap map)
+{
+  SilcMapConnection mapconn;
+  char *hostname, *ip;
+  FILE *fp;
+
+  /* Open for writing */
+  fp = fopen(map->writehtml.filename, "w+");
+  if (!fp) {
+    fprintf(stderr, "Could not open file '%s'\n", map->writehtml.filename);
+    return FALSE;
+  }
+
+  /* Produce a simple HTML index file of all servers */
+
+  fprintf(fp, "<!-- Automatically generated by silcmap --!>\n");
+  fprintf(fp, "<br />\n");
+  fprintf(fp, "<table cellspacing=\"2\" cellpadding=\"2\" border=\"1\">\n");
+  fprintf(fp,
+         "<tr>\n"
+         "<td align=\"center\"><b>Hostname</b></td>\n"
+         "<td align=\"center\"><b>IPv4 Address</b></td>\n"
+         "<td align=\"center\"><b>Port</b></td>\n"
+         "<td align=\"center\"><b>Country</b></td>\n"
+         "<td align=\"center\"><b>Oper</b></td>\n"
+         "</tr>\n");
+
+  silc_dlist_start(map->conns);
+  while ((mapconn = silc_dlist_get(map->conns)) != SILC_LIST_END) {
+
+    silc_dlist_start(mapconn->hostnames);
+    hostname = silc_dlist_get(mapconn->hostnames);
+    silc_dlist_start(mapconn->ips);
+    ip = silc_dlist_get(mapconn->ips);
+
+    fprintf(fp,
+           "<tr>\n"
+           "<td align = \"center\">&nbsp;<a href=\"%s_%d.html\">%s</a></td>\n"
+           "<td align = \"center\">&nbsp;%s</td>\n"
+           "<td align = \"center\">&nbsp;%d</td>\n"
+           "<td align = \"center\">&nbsp;%s</td>\n"
+           "<td align = \"center\">&nbsp;%s</td>\n"
+           "</tr>\n", hostname, mapconn->port,
+           hostname, ip, mapconn->port, mapconn->country, mapconn->admin);
+  }
+
+  fprintf(fp, "</table><br />\n");
+
+  return TRUE;
+}
+
+/* Creates a HTML map file, which can be used to allow user to click
+   URLs on the image at the specified locations. */
+
+bool silc_map_writemaphtml(SilcMap map)
+{
+  SilcMapConnection mapconn;
+  SilcMapCommand cmd;
+  char *hostname, url[256];
+  FILE *fp;
+  int xx , yy, w, h;
+
+  /* Open for writing */
+  fp = fopen(map->writemaphtml.filename, "w+");
+  if (!fp) {
+    fprintf(stderr, "Could not open file '%s'\n", map->writemaphtml.filename);
+    return FALSE;
+  }
+
+  /* The target may be portion of the original map, so we must make the
+     new coordinates relative to the new map. */
+  xx = map->writemaphtml.x;
+  yy = map->writemaphtml.y;
+
+  memset(url, 0, sizeof(url));
+
+  fprintf(fp, "<!-- Automatically generated by silcmap --!>\n");
+  fprintf(fp, "<br />\n");
+  fprintf(fp, "<img src=\"%s\" usemap=\"#map\" border=\"0\">\n",
+         map->writemaphtml.text);
+  fprintf(fp, "<map name=\"map\">\n");
+
+  memset(url, 0, sizeof(url));
+
+  silc_dlist_start(map->conns);
+  while ((mapconn = silc_dlist_get(map->conns)) != SILC_LIST_END) {
+    if (mapconn->writemaphtml_url && mapconn->writemaphtml_url[0]) {
+      silc_strncat(url, sizeof(url), mapconn->writemaphtml_url,
+                  strlen(mapconn->writemaphtml_url));
+    } else {
+      silc_dlist_start(mapconn->hostnames);
+      hostname = silc_dlist_get(mapconn->hostnames);
+      snprintf(url, sizeof(url) - 1, "%s_%d.html", hostname, mapconn->port);
+    }
+
+    /* Print the positions of various items on the map into the map file */
+    silc_dlist_start(mapconn->commands);
+    while ((cmd = silc_dlist_get(mapconn->commands)) != SILC_LIST_END) {
+      if (cmd->draw_text) {
+       w = strlen(cmd->text) * 5;
+       h = map->font.height - 2;
+       fprintf(fp,
+               "<area shape=\"rect\" coords=\"%d,%d,%d,%d\" href=\"%s\">\n",
+               (int)(cmd->x - xx), (int)(cmd->y - yy), w, h, url);
+      }
+
+      if (cmd->draw_circle) {
+       w = 4;
+       fprintf(fp,
+               "<area shape=\"circle\" coords=\"%d,%d,%d\" href=\"%s\">\n",
+               (int)(cmd->x - xx), (int)(cmd->y - yy), w, url);
+       if (cmd->text) {
+         w = strlen(cmd->text) * 5;
+         h = map->font.height - 2;
+         fprintf(fp,
+                 "<area shape=\"rect\" coords=\"%d,%d,%d,%d\" href=\"%s\">\n",
+                 (int)(cmd->x - xx + cmd->lposx),
+                 (int)(cmd->y - yy - cmd->lposy),
+                 (int)(cmd->x - xx + cmd->lposx + w),
+                 (int)(cmd->y - yy - cmd->lposy + h), url);
+       }
+      }
+
+      if (cmd->draw_rectangle) {
+       w = 7;
+       h = 6;
+       fprintf(fp,
+               "<area shape=\"rect\" coords=\"%d,%d,%d,%d\" href=\"%s\">\n",
+               (int)(cmd->x - xx), (int)(cmd->y - yy),
+               (int)(cmd->x - xx + w), (int)(cmd->y - yy + h), url);
+       if (cmd->text) {
+         w = strlen(cmd->text) * 5;
+         h = map->font.height - 2;
+         fprintf(fp,
+                 "<area shape=\"rect\" coords=\"%d,%d,%d,%d\" href=\"%s\">\n",
+                 (int)(cmd->x - xx + cmd->lposx),
+                 (int)(cmd->y - yy - cmd->lposy),
+                 (int)(cmd->x - xx + cmd->lposx + w),
+                 (int)(cmd->y - yy - cmd->lposy + h), url);
+       }
+      }
+    }
+  }
+
+  fprintf(fp, "</map><br />\n");
+  fclose(fp);
+
+  return TRUE;
+}
index 1fb1bdefbbcb1169acfa66370de4fc18816ea6b8..8b0ac3347bfb4281815f44e2f2b233dc7e8d9527 100644 (file)
@@ -1118,6 +1118,7 @@ lib/silcsftp/Makefile
 lib/silcsftp/tests/Makefile
 doc/example_silcd.conf
 includes/silcincludes.h
+silcmap/Makefile
 )
 
 if test "x$silc_dist" = "xsilc-client" ||