From Edgar Gladkich:
authorgerald <gerald@f5534014-38df-0310-8fa8-9805f1628bb7>
Thu, 27 May 2010 15:51:25 +0000 (15:51 +0000)
committergerald <gerald@f5534014-38df-0310-8fa8-9805f1628bb7>
Thu, 27 May 2010 15:51:25 +0000 (15:51 +0000)
This is an extension to the Wireshark context sensitive protocol help. Rows in
TreeView window are analyzed and suitable help file (as HTML) is opened in a
browser.

The help part (large file, 23 MB) of the Protocol Help can be downloaded under
www.inacon.com/dowload/stuff/protocol_help.tar.gz

This protocol help "light" provides descriptive content for the most frequently
used standard protocols, including IP, TCP or SMTP.

From me:

Changes:

Rename "ph_" in some function names to "proto_help_". Move the protocol
help code to its own module.

Make a bunch of functions static. Remove unused code.

Use browser_open_url() instead of a custom function.

Increase the logging levels. Don't clobber the normal log handler.

Update some Doxygen comments to match the format in the rest of the code
base.

Removed GTK version checks. We've been 2.x only for a while.

Move ph_replace_string to string_replace() in epan/strutil.[ch].

Fix a bunch of memory leaks.

Add a NULL pointer check.

Reformat the overview menu label.

Document the file format and locations.

Add Edgar to AUTHORS.

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@32995 f5534014-38df-0310-8fa8-9805f1628bb7

AUTHORS
docbook/wsug_src/WSUG_app_files.xml
epan/strutil.c
epan/strutil.h
gtk/Makefile.common
gtk/main.c
gtk/menus.c
gtk/proto_help.c [new file with mode: 0644]
gtk/proto_help.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index 0f779b068849b37f75cdab0826847712f69ae446..bde42ee95259e4df35839536e21e93918e4c19b5 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -3066,6 +3066,10 @@ H.sivank         <hsivank [AT] gmail.com> {
        GtkOSXApplication support
 }
 
+Edgar Gladkich         <edgar.gladkich [AT] inacon.de> {
+       Protocol help
+}
+
 and by:
 
 Pavel Roskin            <proski [AT] gnu.org>
index 77caee5d939ef9b7c45ad6633dabcbf06dbf8a43..cb85ee663bf4e5d0b678e5713aa0ca9a05666d81 100644 (file)
@@ -527,6 +527,155 @@ c0-a8-1c-00      CEO
        </varlistentry>
       </variablelist>
     </para>
+
+       <section id="ChProtocolHelp"><title>Protocol help configuration</title>
+       <para>
+         Wireshark can use configuration files to create context-sensitive menu
+         items for protocol detail items which will load help URLs in your web
+         browser.
+       </para>
+       <para>
+         To create a protocol help file, create a folder named "protocol_help"
+         in either the personal or global configuration folders. Then create a
+         text file with the extension ".ini" in the "protocol_help" folder. The
+         file must contain key-value pairs with the following sections:
+         <variablelist>
+           <varlistentry>
+             <term>[database]</term>
+             <listitem>
+               <para>
+                 Mandatory. This contains initialization information for the
+                 help file. The following keys must be defined: 
+                 <variablelist>
+                   <varlistentry>
+                     <term>source</term>
+                     <listitem><para>Source name, e.g. "HyperGlobalMegaMart".</para></listitem>
+                   </varlistentry>
+                   <varlistentry>
+                     <term>version</term>
+                     <listitem><para>Must be "1".</para></listitem>
+                   </varlistentry>
+                   <varlistentry>
+                     <term>location</term>
+                     <listitem>
+                       <para>
+                       General URL for help items. Variables can be substituted using
+                       the [location data] section below.
+                       </para>
+                     </listitem>
+                   </varlistentry>
+                 </variablelist>
+               </para>
+             </listitem>
+           </varlistentry>
+
+           <varlistentry>
+             <term>[location data]</term>
+             <listitem>
+               <para>
+                 Optional. Contains keys that will be used for variable
+                 substitution in the "location" value. For example, if
+                 the database section contains
+                 <programlisting>
+locatoin = http://www.example.com/proto?cookie=${cookie}&amp;path=${PATH}
+                 </programlisting>
+                 then setting
+                 <programlisting>
+cookie = anonymous-user-1138
+                 </programlisting>
+                 will result in the URL
+                 "http://www.example.com/proto?cookie=anonymous-user-1138&amp;path=${PATH}".
+                 PATH is used for help path substitution, and shouldn't be defined in this section.
+               </para>
+             </listitem>
+           </varlistentry>
+
+           <varlistentry>
+             <term>[map]</term>
+             <listitem>
+               <para>
+                 Maps Wireshark protocol names to section names below. Each key
+                 MUST match a valid protocol name such as "ip". Each value MUST
+                 have a matching section defined in the configuration file.
+               </para>
+             </listitem>
+           </varlistentry>
+
+         </variablelist>
+
+         Each protocol section must contain an "_OVERVIEW" key which will be used
+         as the first menu item for the help source. Subsequent keys must match
+         descriptions in the protocol detail. Values will be used as the ${PATH}
+         variable in the location template. If ${PATH} isn't present in the location
+         template the value will be appended to the location.
+       </para>
+
+       <para>
+         Suppose the file
+         <filename>C:\Users\sam.clemens\AppData\Roaming\Wireshark\protocol_help\wikipedia.ini</filename>
+         contains the following:
+
+         <programlisting>
+# Wikipedia (en) protocol help file.
+
+# Help file initialization
+# source: The source of the help information, e.g. "Inacon" or "Wikipedia"
+# version: Currently unused. Must be "1".
+# url_template: Template for generated URLs. See "URL Data" below.
+[database]
+source=Wikipedia
+version=1
+url_template=http://${language}.wikipedia.org/wiki/${PATH}
+
+# Substitution data for the location template.
+# Each occurence of the keys below in the locaton template will be
+# substituted with their corresponding values. For example, "${license}"
+# in the URL template above will be replaced with the value of "license"
+# below.
+#
+# PATH is reserved for the help paths below; do not specify it here.
+[location data]
+language = en
+
+# Maps Wireshark protocol names to section names below. Each key MUST match
+# a valid protocol name. Each value MUST have a matching section below.
+[map]
+tcp=TCP
+
+# Mapped protocol sections.
+# Keys must match protocol detail items descriptions.
+[TCP]
+_OVERVIEW=Transmission_Control_Protocol
+Destination port=Transmission_Control_Protocol#TCP_ports
+Source port=Transmission_Control_Protocol#TCP_ports
+         </programlisting>
+         Right-clicking on a TCP protocol detail item will display a help menu
+         item that displays the Wikipedia page for TCP. Right-clicking on the
+         TCP destination or source ports will display additional help menu items that
+         take you to the "TCP ports" section of the page.
+       </para>
+       
+       <para>
+         The [location data] and ${PATH} can be omitted if they are not needed.
+         For example, the following configuration is functionally equivalent to
+         the previous configuration:
+         <programlisting>
+[database]
+source=Wikipedia
+version=1
+location=http://en.wikipedia.org/wiki/
+
+[map]
+tcp=TCP
+
+[TCP]
+_OVERVIEW=Transmission_Control_Protocol
+Destination port=Transmission_Control_Protocol#TCP_ports
+Source port=Transmission_Control_Protocol#TCP_ports
+         </programlisting>
+       </para>
+       </section>
+
        </section>
 
        <section id="ChWindowsFolder"><title>Windows folders</title>
index ab0019cf8aceeae079a2d01308ce508579beffaf..ed81ad135baa4d7a7357370e4712d339e2cc57a4 100644 (file)
@@ -1090,3 +1090,19 @@ ws_strdup_unescape_underscore (const gchar *str)
 
        return new_str;
 }
+
+/* Create a newly-allocated string with replacement values. */
+gchar *string_replace(const gchar* str, const gchar *old_val, const gchar *new_val) {
+       gchar **str_parts;
+       gchar *new_str;
+
+       if (!str || !old_val) {
+               return NULL;
+       }
+       
+       str_parts = g_strsplit(str, old_val, 0);
+       new_str = g_strjoinv(new_val, str_parts);
+       g_strfreev(str_parts);
+       
+       return new_str;
+}
index 4303bf0c43c495b795ca38bc837ac5f78bfce59e..27c236130938c197199d68e03855ec702dc3d642 100644 (file)
@@ -244,4 +244,15 @@ gchar* ws_strdup_escape_underscore (const gchar *str);
  */
 gchar* ws_strdup_unescape_underscore (const gchar *str);
 
+/** Replace values in a string
+ *
+ * @param str String containing 0 or more values to be replaced.
+ * @param old_val Old value.
+ * @param new_val New value. May be NULL, in which case occurences of
+ *                           old_value will be removed.
+ * @return A newly-allocated version of str with replacement values or
+ * NULL on failure.
+ */
+gchar *string_replace(const gchar* str, const gchar *old_val, const gchar *new_val);
+
 #endif /* __STRUTIL_H__ */
index b4ae7640e6e201382887d3d1d8bb273be5a216d5..27fed0bec300a582999e34955972dc71a7535c7d 100644 (file)
@@ -108,6 +108,7 @@ WIRESHARK_GTK_SRC = \
        profile_dlg.c   \
        progress_dlg.c  \
        proto_dlg.c     \
+       proto_help.c    \
        proto_hier_stats_dlg.c  \
        range_utils.c   \
        recent.c        \
@@ -296,6 +297,7 @@ noinst_HEADERS = \
        prefs_stream.h  \
        profile_dlg.h   \
        proto_dlg.h     \
+       proto_help.h    \
        proto_hier_stats_dlg.h  \
        range_utils.h \
        recent.h        \
index 510c4d8704529ec7f8809f5b6c7fdf94c1fd8bc3..45e36a8512e23df7b2e64e13be3780b754af0f4c 100644 (file)
 #include "gtk/tap_dfilter_dlg.h"
 #include "gtk/prefs_column.h"
 #include "gtk/prefs_dlg.h"
+#include "gtk/proto_help.h"
 
 #ifdef HAVE_LIBPCAP
 #include "../image/wsicon16.xpm"
@@ -807,6 +808,7 @@ tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data _U_)
         cf_unselect_field(&cfile);
         packet_hex_print(byte_view, byte_data,
                          cfile.current_frame, NULL, byte_len);
+        proto_help_menu_modify(sel, &cfile);
         return;
     }
     gtk_tree_model_get(model, &iter, 1, &finfo, -1);
@@ -870,6 +872,7 @@ tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data _U_)
     }
     packet_hex_print(byte_view, byte_data, cfile.current_frame, finfo,
                      byte_len);
+    proto_help_menu_modify(sel, &cfile);
 }
 
 void collapse_all_cb(GtkWidget *widget _U_, gpointer data _U_) {
@@ -2417,6 +2420,8 @@ main(int argc, char *argv[])
                  rf_path, strerror(rf_open_errno));
   }
 
+  proto_help_init();
+
   cap_file_init(&cfile);
 
   /* Fill in capture options with values from the preferences */
index c0b5b722c25584f4b370ad30d35893eb686b0d1c..ad59431b78bc930d2327ba586ba11b9f2a54bbd2 100644 (file)
@@ -94,6 +94,7 @@
 #include "gtk/uat_gui.h"
 #include "gtk/gui_utils.h"
 #include "gtk/manual_addr_resolv.h"
+#include "gtk/proto_help.h"
 
 #ifdef NEW_PACKET_LIST
 #include "gtk/new_packet_list.h"
@@ -1140,6 +1141,7 @@ static GtkItemFactoryEntry tree_view_menu_items[] =
                        0, "<StockItem>", WIRESHARK_STOCK_WIKI,},
     {"/Filter Field Reference", NULL, GTK_MENU_FUNC(selected_ptree_ref_cb),
                        0, "<StockItem>", WIRESHARK_STOCK_INTERNET,},
+    {"/Protocol Help", NULL, NULL, 0, "<Branch>", NULL,},
     {"/Protocol Preferences", NULL, NULL, 0, NULL, NULL,},
     {"/<separator>", NULL, NULL, 0, "<Separator>", NULL,},
     {"/Decode As...", NULL, GTK_MENU_FUNC(decode_as_cb), 0, "<StockItem>", WIRESHARK_STOCK_DECODE_AS,},
@@ -1378,9 +1380,11 @@ menus_init(void) {
     set_menus_for_capture_in_progress(FALSE);
     set_menus_for_file_set(/* dialog */TRUE, /* previous file */ FALSE, /* next_file */ FALSE);
 
-    /* init with an empty recent files list */
+    /* Init with an empty recent files list */
     clear_menu_recent_capture_file_cmd_cb(NULL, NULL);
 
+    /* Protocol help links */
+    proto_help_menu_init(tree_view_menu_factory->widget);
     }
 }
 
diff --git a/gtk/proto_help.c b/gtk/proto_help.c
new file mode 100644 (file)
index 0000000..af25ef8
--- /dev/null
@@ -0,0 +1,662 @@
+/* proto_help.c
+ * Routines for dynamic protocol help menus
+ *
+ * $Id: capture_dlg.c 32829 2010-05-16 08:14:29Z guy $
+ *
+ * Edgar Gladkich <edgar.gladkich@incacon.de>
+ * Gerald Combs <gerald@wireshark.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gtk/webbrowser.h>
+
+#include "../file.h"
+
+#include <epan/prefs.h>
+#include <epan/filesystem.h>
+#include <epan/strutil.h>
+#include <epan/proto.h>
+
+#define PH_MENU_TOP "/Protocol Help"
+
+#define PH_FILE_LOG "ph.log"
+#define PH_INI_SUFFIX ".ini"
+#define PH_PATH_SEARCH_STR "${PATH}"
+
+/* .ini Section names */
+#define PH_INI_GROUP_DATABASE "database"
+#define PH_INI_GROUP_LOCATION_DATA "location data"
+#define PH_INI_GROUP_MAP "map"
+
+/* .ini [database] section keys */
+#define PH_INI_DB_KEY_SOURCE "source"
+#define PH_INI_DB_KEY_LOCATION_TEMPLATE "location"
+
+/* .ini Path sections */
+#define PH_INI_KEY_OVERVIEW "_OVERVIEW"
+
+/* Where to look for .ini files */
+#define PH_CONFFILE_SUBDIR "protocol_help"
+
+#ifdef PH_DEBUG_LOG
+const gchar *ph_log_path;
+#endif
+
+typedef struct proto_help_key_file_t {
+       GKeyFile *keyfile;
+       const gchar *source;
+       const gchar *loc_template;
+} proto_help_key_file;
+
+GPtrArray *g_ph_key_files = NULL;
+
+GtkItemFactory *g_ph_menu_factory;
+
+static void ph_menu_reset(void);
+static void ph_menu_onclick(GtkWidget*, gpointer, guint);
+
+static int ph_capture_get_protocol_id(GtkTreeSelection*, capture_file*);
+static const gchar* ph_capture_get_protocol_name(GtkTreeSelection*, capture_file*);
+static const gchar* ph_capture_get_protocol_abbrev(GtkTreeSelection*, capture_file*);
+static gchar* ph_capture_get_description(capture_file*);
+
+static proto_help_key_file *ph_ini_load_file(const gchar*);
+static gchar* ph_ini_get_value(GKeyFile *, const gchar*, const gchar*, gchar *);
+static gchar* ph_ini_get_path(GKeyFile *, const gchar*, const gchar*);
+static gchar** ph_ini_get_keywords(GKeyFile *, const gchar*);
+
+static guint ph_parse_string(const gchar*, const gchar*);
+
+/* #define PH_DEBUG_LOG 1 */
+#ifdef PH_DEBUG_LOG
+static void ph_logging_handler(const gchar*, GLogLevelFlags, const gchar*, gpointer);
+#endif
+
+/** @file
+ * Protocol help routines. Adds web browser links to protocol menu items
+ * via configuration files.
+ */
+/** Initialization
+ * 
+ * @param void
+ * @return void
+ */
+#define PH_CONF_DIRS 2
+void proto_help_init(void)
+{
+       gchar *search_dir[PH_CONF_DIRS];
+       const gchar *ini_name;
+       gchar *ini_path;
+       GDir *conf_dir;
+       proto_help_key_file *phkf;
+       int i;
+
+       search_dir[0] = g_strdup_printf("%s" G_DIR_SEPARATOR_S PH_CONFFILE_SUBDIR, get_datafile_dir());
+       /* XXX - Use profiles? */
+       search_dir[1] = get_persconffile_path(PH_CONFFILE_SUBDIR, FALSE, FALSE);
+
+#ifdef PH_DEBUG_LOG
+       g_log_set_handler(NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, ph_logging_handler, NULL);
+#endif
+
+       if (g_ph_key_files)
+               return;
+
+       g_ph_key_files = g_ptr_array_new();
+       
+       /* Start loop */
+
+#ifdef PH_DEBUG_LOG
+       ph_log_path = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", g_get_tmp_dir(), PH_FILE_LOG);
+#endif
+
+       for (i = 0; i < PH_CONF_DIRS; i++) {
+               g_log(NULL, G_LOG_LEVEL_INFO, "Looking for protocol help files in '%s'", search_dir[i]);
+               conf_dir = g_dir_open(search_dir[i], 0, NULL);
+               if (!conf_dir) {
+                       continue;
+               }
+
+               while ((ini_name = g_dir_read_name(conf_dir)) != NULL) {
+                       if (! g_str_has_suffix(ini_name, PH_INI_SUFFIX)) {
+                               continue;
+                       }
+                       g_log(NULL, G_LOG_LEVEL_INFO, "-- Found '%s'", ini_name);
+                       ini_path = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", search_dir[i], ini_name);
+                       phkf = ph_ini_load_file(ini_path);
+                       g_free(ini_path);
+               }
+               g_dir_close(conf_dir);
+       }
+}
+
+/** Initialize the menu
+ * 
+ * @param widget Context menu root
+ * @return void
+ */
+void proto_help_menu_init(GtkWidget *widget)
+{
+       g_ph_menu_factory = gtk_item_factory_from_widget(widget);
+       ph_menu_reset();
+}
+
+/** Clear the menu
+ * 
+ * @param void
+ * @return void
+ */
+static void ph_menu_reset(void)
+{
+       GtkWidget *menu_item = NULL;
+       GList *menu_entries = NULL;
+       GList *menu_entry = NULL;
+
+       if(!g_ph_menu_factory) return;
+
+       menu_item = gtk_item_factory_get_widget(g_ph_menu_factory, PH_MENU_TOP);
+       menu_entries = gtk_container_children(GTK_CONTAINER(menu_item));
+
+       for(menu_entry = g_list_first(menu_entries); menu_entry != NULL; menu_entry = g_list_next(menu_entry))
+       {
+               gtk_container_remove(GTK_CONTAINER(menu_item), menu_entry->data);
+       }
+
+       menu_item = gtk_item_factory_get_item(g_ph_menu_factory, PH_MENU_TOP);
+       gtk_widget_set_sensitive(menu_item, FALSE);
+}
+
+/* Callback to free URLs */
+static void
+url_destroy_cb(GtkWidget *w _U_, gpointer url) {
+       g_free(url);
+}
+
+/** Fill in the protocol help menu
+ * 
+ * @param selection Currently-selected packet
+ * @param cf Capture file
+ * @return void
+ */
+void proto_help_menu_modify(GtkTreeSelection *selection, capture_file *cf)
+{
+       gchar *description;
+       const gchar *proto_abbrev, *proto_name;
+       gchar *value;
+       gchar **keys;
+       GHashTable *table;
+       guint i = 0, cur_kf;
+       GtkWidget *menu_item = NULL;
+       GtkItemFactoryEntry *menu_entry = NULL;
+       proto_help_key_file* phkf;
+       gchar *loc;
+       gboolean add_separator = FALSE;
+       gboolean found = FALSE;
+
+       if(!g_ph_menu_factory) return;
+       ph_menu_reset();
+
+       proto_abbrev = ph_capture_get_protocol_abbrev(selection, cf);
+       if(!proto_abbrev) return;
+
+       proto_name = ph_capture_get_protocol_name(selection, cf);
+       if(!proto_name) return;
+
+       description = ph_capture_get_description(cf);
+
+       for (cur_kf = 0; cur_kf < g_ph_key_files->len; cur_kf++) {
+               phkf = (proto_help_key_file *) g_ptr_array_index(g_ph_key_files, cur_kf);
+               g_assert(phkf);
+
+               value = ph_ini_get_path(phkf->keyfile, proto_abbrev, PH_INI_KEY_OVERVIEW);
+               
+               if(!value)
+               {
+                       g_log(NULL, G_LOG_LEVEL_DEBUG, "Overview page of the protocol '%s' is not defined", proto_abbrev);
+                       continue;
+               }
+
+               /*
+                * XXX - We could save some memory here if we stored the location template
+                * and search value as separate items. However, that makes freeing them
+                * a bit messy.
+                */
+               loc = string_replace(phkf->loc_template, PH_PATH_SEARCH_STR, value);
+               g_free(value);
+               if (!loc || !strlen(loc)) continue;
+
+               if (add_separator) {
+                       menu_entry = g_malloc0(sizeof(GtkItemFactoryEntry));
+                       menu_entry->path = g_strdup_printf("%s/<separator>", PH_MENU_TOP);
+                       menu_entry->item_type = "<Separator>";
+                       gtk_item_factory_create_item(g_ph_menu_factory, menu_entry, NULL, 2);
+               }
+               add_separator = TRUE;
+
+               menu_entry = g_malloc0(sizeof(GtkItemFactoryEntry));
+               menu_entry->path = g_strdup_printf("%s/%s %s Overview", PH_MENU_TOP, phkf->source, proto_name);
+               menu_entry->callback = ph_menu_onclick;
+               gtk_item_factory_create_item(g_ph_menu_factory, menu_entry, loc, 2);
+               menu_item = gtk_item_factory_get_widget(g_ph_menu_factory, menu_entry->path);
+               g_assert(menu_item);
+               g_signal_connect(menu_item, "destroy", G_CALLBACK(url_destroy_cb), loc);
+
+               found = TRUE;
+
+               if(description)
+               {
+                       keys = ph_ini_get_keywords(phkf->keyfile, proto_abbrev);
+                       table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+       
+                       for(i = 0; keys[i] != NULL; i++)
+                       {
+                               if(!strcmp(keys[i], PH_INI_KEY_OVERVIEW)) continue; /* We already added the overview */
+                               if(!ph_parse_string(description, keys[i])) continue; /* Bad format */
+                               if(g_hash_table_lookup(table, g_ascii_strup(keys[i], -1)) != NULL) continue; /* Duplicate */
+
+                               value = ph_ini_get_path(phkf->keyfile, proto_abbrev, keys[i]);
+                               if(!value || !strlen(value)) continue;
+                               
+                               loc = string_replace(phkf->loc_template, PH_PATH_SEARCH_STR, value);
+                               g_free(value);
+                               if (!loc || !strlen(loc)) continue;
+
+                               g_hash_table_insert(table, g_ascii_strup(keys[i], -1), GINT_TO_POINTER(1));
+
+                               menu_entry = g_malloc0(sizeof(GtkItemFactoryEntry));
+                               menu_entry->path = g_strdup_printf("%s/%s", PH_MENU_TOP, keys[i]);
+                               menu_entry->callback = ph_menu_onclick;
+                               gtk_item_factory_create_item(g_ph_menu_factory, menu_entry, loc, 2);
+                               menu_item = gtk_item_factory_get_widget(g_ph_menu_factory, menu_entry->path);
+                               g_assert(menu_item);
+                               g_signal_connect(menu_item, "destroy", G_CALLBACK(url_destroy_cb), loc);
+                       }
+       
+                       g_hash_table_destroy(table);
+               }
+       }
+
+       g_free(description);
+       menu_item = gtk_item_factory_get_item(g_ph_menu_factory, PH_MENU_TOP);
+       gtk_widget_set_sensitive(menu_item, found);
+}
+
+/**
+* Function ph_menu_onclick
+* 
+* @param GtkWidget *widget Description
+* @param gpointer data Description
+* @param guint action Description
+* @return void
+*/
+static void ph_menu_onclick(GtkWidget *widget _U_, gpointer data, guint action _U_)
+{
+       gchar *loc = (gchar *) data;
+       
+       g_log(NULL, G_LOG_LEVEL_DEBUG, "Sending '%s' to the browser.", loc);
+
+       if (! loc) {
+               g_log(NULL, G_LOG_LEVEL_DEBUG, "Protocol help ended up with a NULL URL.");
+               return;
+       }
+
+       /* XXX - Should we do any RFC 3986 escaping first? */
+       if (! browser_open_url(loc)) {
+               g_log(NULL, G_LOG_LEVEL_DEBUG, "Couldn't get protocol help for %s", loc);
+       }
+}
+
+/** Get the field ID for a selected tree item.
+ * 
+ * @param selection Tree selection
+ * @param cfile Capture file
+ * @return Field ID or 0
+ */
+static int
+ph_capture_get_protocol_id(GtkTreeSelection *selection, capture_file *cf)
+{
+       GtkTreeModel *model = NULL;
+       GtkTreeIter iter;
+       GtkTreeIter parent;
+       field_info *finfo = NULL;
+       int proto_id = 0;
+
+       if(!cf->finfo_selected) return 0;
+       proto_id = cf->finfo_selected->hfinfo->id;
+
+       if(!proto_id)
+       {
+               if(!gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection), &model, &iter)) return 0;
+
+               while(gtk_tree_model_iter_parent(model, &parent, &iter))
+               {
+                       gtk_tree_model_get(model, &parent, 1, &finfo, -1);
+                       iter = parent;
+
+                       if(finfo->hfinfo->id > 0)
+                       {
+                               proto_id = finfo->hfinfo->id;
+                               break;
+                       }
+               }
+       }
+
+       while(proto_id && !proto_registrar_is_protocol(proto_id))
+       {
+               proto_id = proto_registrar_get_parent(proto_id);
+       }
+       
+       return proto_id;
+}
+
+/** Get the protocol name for a selected tree item.
+ * 
+ * @param selection Tree selection
+ * @param cfile Capture file
+ * @return Name for a protocol or NULL
+ */
+static const gchar*
+ph_capture_get_protocol_name(GtkTreeSelection *selection, capture_file *cf)
+{
+       int proto_id = ph_capture_get_protocol_id(selection, cf);
+
+       return (!proto_id) ? NULL : proto_get_protocol_short_name(find_protocol_by_id(proto_id));
+}
+
+/** Get the abbreviated protocol name for a selected tree item.
+ * 
+ * @param selection Tree selection
+ * @param cfile Capture file
+ * @return Abbreviated (lower-case) name for a protocol or NULL
+ */
+static const gchar*
+ph_capture_get_protocol_abbrev(GtkTreeSelection *selection, capture_file *cf)
+{
+       int proto_id = ph_capture_get_protocol_id(selection, cf);
+
+       return (!proto_id) ? NULL : proto_registrar_get_abbrev(proto_id);
+}
+
+/** Return the selected item description
+ * @param cf capture file pointer
+ * @return Description of the selected item. MUST be freed with g_free().
+ */
+static gchar* ph_capture_get_description(capture_file *cf)
+{
+       gchar *buffer = NULL;
+       
+       if(cf->finfo_selected->rep->representation != 0)
+       {
+               buffer = g_strdup(cf->finfo_selected->rep->representation);
+       }
+       else
+       {
+               buffer = g_malloc(ITEM_LABEL_LENGTH);
+               proto_item_fill_label(cf->finfo_selected, buffer);
+       }
+       
+       return buffer;
+}
+
+/** Load a protocol help key file and add it to the global array.
+ * 
+ * @param filename Full path to the key file.
+ * @return Newly-created key file entry or NULL.
+ */
+static proto_help_key_file *
+ph_ini_load_file(const gchar *filename)
+{
+       GKeyFile *kf;
+       GError *error = NULL;
+       proto_help_key_file *phkf = NULL;
+       gchar *old_template, *loc_template;
+       gchar **loc_data, *loc_repl, *loc_search;
+       gsize i, len;
+
+       if(!g_file_test(filename, G_FILE_TEST_EXISTS))
+       {
+               g_log(NULL, G_LOG_LEVEL_DEBUG, "Configuration file %s does not exists", filename);
+               return NULL;
+       }
+
+       kf = g_key_file_new();
+
+       if(!g_key_file_load_from_file(kf, filename, G_KEY_FILE_NONE, &error))
+       {
+               g_log(NULL, G_LOG_LEVEL_DEBUG, "Configuration file '%s' could not be loaded (%s)", filename, error->message);
+               g_error_free(error);
+               g_key_file_free(kf);
+
+               return NULL;
+       }
+
+       loc_template = ph_ini_get_value(kf, PH_INI_GROUP_DATABASE, PH_INI_DB_KEY_LOCATION_TEMPLATE, NULL);
+       if (!loc_template) {
+               g_key_file_free(kf);
+               return NULL;
+       }
+
+       loc_data = g_key_file_get_keys(kf, PH_INI_GROUP_LOCATION_DATA, &len, NULL);
+       if (loc_data) {
+               for (i = 0; i < len; i++) {
+                       loc_repl = ph_ini_get_value(kf, PH_INI_GROUP_LOCATION_DATA, loc_data[i], NULL);
+                       old_template = loc_template;
+                       loc_search = g_strdup_printf("${%s}", loc_data[i]);
+                       loc_template = string_replace(loc_template, loc_search, loc_repl);
+                       g_free(loc_repl);
+                       g_free(loc_search);
+                       g_free(old_template);
+               }
+       }
+
+       /* Add ${PATH} to the end if it's not present */
+       if (!strstr(loc_template, PH_PATH_SEARCH_STR)) {
+               old_template = loc_template;
+               loc_template = g_strdup_printf("%s" PH_PATH_SEARCH_STR, old_template);
+               g_free(old_template);
+       }
+
+       phkf = g_malloc(sizeof(proto_help_key_file));
+       phkf->keyfile = kf;
+       phkf->source = ph_ini_get_value(kf, PH_INI_GROUP_DATABASE, PH_INI_DB_KEY_SOURCE, "(Unknown source)");
+       phkf->loc_template = loc_template;
+       
+       g_ptr_array_add(g_ph_key_files, phkf);
+
+       return phkf;
+}
+
+/** Fetch a value for a key from a key file with an optional default value.
+ *
+ * @param keyfile The key file to search
+ * @param group Key file group
+ * @param key Key file value to fetch
+ * @param alt Alternate string to return. May be NULL.
+ * @return const gchar* A newly-allocated key value, or a copy of alt if not found.
+ */
+static gchar*
+ph_ini_get_value(GKeyFile *keyfile, const gchar *group, const gchar *key, gchar *alt)
+{
+       gchar *value = NULL;
+
+       if (keyfile) {
+               value = g_key_file_get_string(keyfile, group, key, NULL);
+       }
+       
+       if (!value) {
+               value = g_strdup(alt);
+       }
+
+       return value;
+}
+
+/** Given a protocol name and a key, map the protocol name to a section in the
+ * keyfile, then look up the value for that key.
+ *
+ * @param keyfile The key file to search
+ * @param protocol Wireshark protocol name to map, e.g. "tcp".
+ * @param keyword The key to fetch from the mapped section.
+ * @return 
+ */
+static gchar*
+ph_ini_get_path(GKeyFile *keyfile, const gchar *protocol, const gchar *keyword)
+{
+       GError *error = NULL;
+       gchar *map;
+       gchar *value;
+
+       if(!keyfile || !protocol || !keyword) return NULL;
+
+       map = g_key_file_get_string(keyfile, PH_INI_GROUP_MAP, protocol, &error);
+
+       if(!map)
+       {
+               g_log(NULL, G_LOG_LEVEL_DEBUG, "Protocol '%s' is not defined (%s)", protocol, error->message);
+               g_error_free(error);
+               return NULL;
+       }
+
+       value = g_key_file_get_string(keyfile, map, keyword, NULL);
+       g_free(map);
+       return value;
+}
+
+/** Given a protocol name, map it to a section in the keyfile, then
+ * return the keys in that section.
+ * 
+ * @param keyfile The key file to search
+ * @param protocol Wireshark protocol name to map, e.g. "tcp".
+ * @return An array of keys in the mapped section. Must be freed with g_strfreev().
+ */
+static gchar**
+ph_ini_get_keywords(GKeyFile *keyfile, const gchar *protocol)
+{
+       GError *error = NULL;
+       gchar *map;
+       gchar **keys;
+       gsize length = 0;
+
+       if(!keyfile) return NULL;
+       
+       map = g_key_file_get_string(keyfile, PH_INI_GROUP_MAP, protocol, &error);
+
+       if(!map)
+       {
+               g_log(NULL, G_LOG_LEVEL_DEBUG, "Protocol '%s' is not defined (%s)", protocol, error->message);
+               g_error_free(error);
+               return NULL;
+       }
+
+       error = NULL;
+       keys = g_key_file_get_keys(keyfile, map, &length, &error);
+       g_free(map);
+
+       if(!keys)
+       {
+               g_log(NULL, G_LOG_LEVEL_DEBUG, "Display titles are not defined (%s)", protocol);
+               g_error_free(error);
+       }
+       
+       return keys;
+}
+
+/**
+* Function ph_parse_string
+* 
+* @param const gchar *description Description
+* @param const gchar *value Description
+* @return guint Description
+*/
+static guint ph_parse_string(const gchar *description, const gchar *value)
+{
+       GRegex *regex = NULL;
+       GMatchInfo *match_info = NULL;
+       gchar *pattern = NULL;
+       guint result = 0;
+
+       pattern = g_strdup_printf("(?<![0-9a-zA-Z_])%s(?![0-9a-zA-Z_])", value);
+       regex = g_regex_new(pattern, 0, 0, NULL);
+
+       g_regex_match(regex, description, 0, &match_info);
+       if(g_match_info_matches(match_info)) result = 1;
+
+       g_match_info_free(match_info);
+       g_regex_unref(regex);
+
+       return result;
+}
+
+#ifdef PH_DEBUG_LOG
+/**
+ * Function ph_logging_handler
+ * 
+ * @param const gchar *domain Description
+ * @param GLogLevelFlags level Description
+ * @param const gchar *message Description
+ * @param gpointer data Description
+ * @return void
+ */
+static void ph_logging_handler(const gchar *domain _U_, GLogLevelFlags level, const gchar *message, gpointer data _U_)
+{
+       gchar *log = NULL;
+       gchar *type = NULL;
+       FILE *file = NULL;
+       struct tm *timestamp = NULL;
+       time_t now;
+       
+       time(&now);
+       timestamp = localtime(&now);
+
+       switch(level & G_LOG_LEVEL_MASK)
+       {
+               case G_LOG_LEVEL_ERROR:
+                       type = "ERR";
+                       break;
+
+               case G_LOG_LEVEL_DEBUG:
+                       type = "WARNING";
+                       break;
+
+               case G_LOG_LEVEL_INFO:
+                       type = "INFO";
+                       break;
+
+               default:
+                       type = "OTHER";
+       }
+
+       file = fopen(ph_log_path, "a+");
+       
+       if(file)
+       {
+               log = g_strdup_printf("[%04u-%02u-%02u %02u:%02u:%02u %s] %s\n", timestamp->tm_year + 1900, timestamp->tm_mon + 1, timestamp->tm_mday, timestamp->tm_hour, timestamp->tm_min, timestamp->tm_sec, type, message);
+               fputs(log, file);
+               fclose(file);
+       }
+}
+#endif
\ No newline at end of file
diff --git a/gtk/proto_help.h b/gtk/proto_help.h
new file mode 100644 (file)
index 0000000..e11681a
--- /dev/null
@@ -0,0 +1,46 @@
+/* proto_help.h
+ * Routines for dynamic protocol help menus
+ *
+ * $Id: capture_dlg.c 32829 2010-05-16 08:14:29Z guy $
+ *
+ * Edgar Gladkich <edgar.gladkich@incacon.de>
+ * Gerald Combs <gerald@wireshark.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+/** Search for and read configuration files
+ * 
+ */
+extern void proto_help_init(void);
+
+/** Initialize the menu
+ * 
+ * @param widget Context menu root
+ * @return void
+ */
+extern void proto_help_menu_init(GtkWidget *);
+
+/** Fill in the protocol help menu
+ * 
+ * @param selection Currently-selected packet
+ * @param cf Capture file
+ * @return void
+ */
+extern void proto_help_menu_modify(GtkTreeSelection*, capture_file *cfile);