add parameter dcerpc_info to PIDL_dissect_ipv?address()
[wireshark-wip.git] / ui / gtk / proto_help.c
blobbbf5fe04d03e8fd8007d91cac20ee4f9f9a335f7
1 /* proto_help.c
2 * Routines for dynamic protocol help menus
4 * $Id$
6 * Edgar Gladkich <edgar.gladkich@incacon.de>
7 * Gerald Combs <gerald@wireshark.org>
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "config.h"
29 /* This file basically does nothing except remind us how this worked before
30 * the menu code was rewritten to no longer use the deprecated ItemFactory
31 * stuff.
33 #define MAIN_MENU_USE_UIMANAGER 1
35 #include <string.h>
37 #include <gtk/gtk.h>
38 #include "ui/gtk/webbrowser.h"
40 #include "../file.h"
42 #include <epan/prefs.h>
43 #include <epan/filesystem.h>
44 #include <epan/strutil.h>
45 #include <epan/proto.h>
47 #include "ui/gtk/proto_help.h"
50 * The protocol help files cannot be downloaded downloaded directly. You
51 * will need to go to http://www.inacon.com/download/, then download one
52 * of the packages, e.g. the source package and extract the protocol_help
53 * folder. To actually use the help you will also need to be able to
54 * access the URL beginning at the contents of the loclation line in the
55 * ph.ini file (e.g. location=http://www.inacon.de/ph/data/).
56 * To provide offline service download the contents of the given location
57 * to your computer, then change the location parameter in the ph.ini file
58 * to point to that new URL. This may be a file:/// URL.
61 /* Right now proto_help will not build with -DGTK_DISABLE_DEPRECATED due to
62 its use of GtkItemFactory.
63 See http://developer.gnome.org/gtk/2.24/GtkItemFactory.html: "As of GTK+ 2.4,
64 GtkItemFactory has been deprecated in favour of GtkUIManager."
65 This needs to be rewritten by the people who added this code or some other
66 volunteers. Otherwise this functionality will be lost once the non UI_MANAGER
67 stuff gets removed it isn't built by default any more.
69 #ifdef MAIN_MENU_USE_UIMANAGER
70 void proto_help_menu_modify(GtkTreeSelection *selection _U_, capture_file *cf _U_) {}
71 void proto_help_menu_init(GtkWidget *widget _U_) {}
72 void proto_help_init(void) {}
73 #else
75 #define PH_MENU_TOP "/Protocol Help"
77 #define PH_FILE_LOG "ph.log"
78 #define PH_INI_SUFFIX ".ini"
79 #define PH_PATH_SEARCH_STR "${PATH}"
81 /* .ini Section names */
82 #define PH_INI_GROUP_DATABASE "database"
83 #define PH_INI_GROUP_LOCATION_DATA "location data"
84 #define PH_INI_GROUP_MAP "map"
86 /* .ini [database] section keys */
87 #define PH_INI_DB_KEY_SOURCE "source"
88 #define PH_INI_DB_KEY_LOCATION_TEMPLATE "location"
90 /* .ini Path sections */
91 #define PH_INI_KEY_OVERVIEW "_OVERVIEW"
93 /* Where to look for .ini files */
94 #define PH_CONFFILE_SUBDIR "protocol_help"
96 #ifdef PH_DEBUG_LOG
97 const gchar *ph_log_path;
98 #endif
100 typedef struct proto_help_key_file_t {
101 GKeyFile *keyfile;
102 const gchar *source;
103 const gchar *loc_template;
104 } proto_help_key_file;
106 GPtrArray *g_ph_key_files = NULL;
108 GtkItemFactory *g_ph_menu_factory;
110 static void ph_menu_reset(void);
111 static void ph_menu_onclick(GtkWidget*, gpointer, guint);
113 static int ph_capture_get_protocol_id(GtkTreeSelection*, capture_file*);
114 static const gchar* ph_capture_get_protocol_name(GtkTreeSelection*, capture_file*);
115 static const gchar* ph_capture_get_protocol_abbrev(GtkTreeSelection*, capture_file*);
116 static gchar* ph_capture_get_description(capture_file*);
118 static proto_help_key_file *ph_ini_load_file(const gchar*);
119 static gchar* ph_ini_get_value(GKeyFile *, const gchar*, const gchar*, gchar *);
120 static gchar* ph_ini_get_path(GKeyFile *, const gchar*, const gchar*);
121 static gchar** ph_ini_get_keywords(GKeyFile *, const gchar*);
123 static guint ph_parse_string(const gchar*, const gchar*);
125 /* #define PH_DEBUG_LOG 1 */
126 #ifdef PH_DEBUG_LOG
127 static void ph_logging_handler(const gchar*, GLogLevelFlags, const gchar*, gpointer);
128 #endif
130 /** @file
131 * Protocol help routines. Adds web browser links to protocol menu items
132 * via configuration files.
135 /** Initialization
137 * @param void
138 * @return void
140 #define PH_CONF_DIRS 2
141 void proto_help_init(void)
143 gchar *search_dir[PH_CONF_DIRS];
144 const gchar *ini_name;
145 gchar *ini_path;
146 GDir *conf_dir;
147 int i;
149 search_dir[0] = g_strdup_printf("%s" G_DIR_SEPARATOR_S PH_CONFFILE_SUBDIR, get_datafile_dir());
150 /* XXX - Use profiles? */
151 search_dir[1] = get_persconffile_path(PH_CONFFILE_SUBDIR, FALSE, FALSE);
153 #ifdef PH_DEBUG_LOG
154 g_log_set_handler(NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, ph_logging_handler, NULL);
155 #endif
157 if (g_ph_key_files)
158 return;
160 g_ph_key_files = g_ptr_array_new();
162 /* Start loop */
164 #ifdef PH_DEBUG_LOG
165 ph_log_path = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", g_get_tmp_dir(), PH_FILE_LOG);
166 #endif
168 for (i = 0; i < PH_CONF_DIRS; i++) {
169 g_log(NULL, G_LOG_LEVEL_INFO, "Looking for protocol help files in '%s'", search_dir[i]);
170 conf_dir = g_dir_open(search_dir[i], 0, NULL);
171 if (!conf_dir) {
172 continue;
175 while ((ini_name = g_dir_read_name(conf_dir)) != NULL) {
176 if (! g_str_has_suffix(ini_name, PH_INI_SUFFIX)) {
177 continue;
179 g_log(NULL, G_LOG_LEVEL_INFO, "-- Found '%s'", ini_name);
180 ini_path = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", search_dir[i], ini_name);
181 ph_ini_load_file(ini_path);
182 g_free(ini_path);
184 g_dir_close(conf_dir);
188 /** Initialize the menu
190 * @param widget Context menu root
191 * @return void
193 void proto_help_menu_init(GtkWidget *widget)
195 g_ph_menu_factory = gtk_item_factory_from_widget(widget);
196 ph_menu_reset();
199 /** Clear the menu
201 * @param void
202 * @return void
204 static void ph_menu_reset(void)
206 GtkWidget *menu_item = NULL;
207 GList *menu_entries = NULL;
208 GList *menu_entry = NULL;
210 if(!g_ph_menu_factory) return;
212 menu_item = gtk_item_factory_get_widget(g_ph_menu_factory, PH_MENU_TOP);
213 menu_entries = gtk_container_get_children(GTK_CONTAINER(menu_item));
215 for(menu_entry = g_list_first(menu_entries); menu_entry != NULL; menu_entry = g_list_next(menu_entry))
217 gtk_container_remove(GTK_CONTAINER(menu_item), menu_entry->data);
220 menu_item = gtk_item_factory_get_item(g_ph_menu_factory, PH_MENU_TOP);
221 gtk_widget_set_sensitive(menu_item, FALSE);
224 /* Callback to free URLs */
225 static void
226 url_destroy_cb(GtkWidget *w _U_, gpointer url) {
227 g_free(url);
230 /** Fill in the protocol help menu
232 * @param selection Currently-selected packet
233 * @param cf Capture file
234 * @return void
236 void proto_help_menu_modify(GtkTreeSelection *selection, capture_file *cf)
238 gchar *description;
239 const gchar *proto_abbrev, *proto_name;
240 gchar *value;
241 gchar **keys;
242 GHashTable *table;
243 guint i = 0, cur_kf;
244 GtkWidget *menu_item = NULL;
245 GtkItemFactoryEntry *menu_entry = NULL;
246 proto_help_key_file* phkf;
247 gchar *loc;
248 gboolean add_separator = FALSE;
249 gboolean found = FALSE;
251 if(!g_ph_menu_factory) return;
252 ph_menu_reset();
254 proto_abbrev = ph_capture_get_protocol_abbrev(selection, cf);
255 if(!proto_abbrev) return;
257 proto_name = ph_capture_get_protocol_name(selection, cf);
258 if(!proto_name) return;
260 description = ph_capture_get_description(cf);
262 for (cur_kf = 0; cur_kf < g_ph_key_files->len; cur_kf++) {
263 phkf = (proto_help_key_file *) g_ptr_array_index(g_ph_key_files, cur_kf);
264 g_assert(phkf);
266 value = ph_ini_get_path(phkf->keyfile, proto_abbrev, PH_INI_KEY_OVERVIEW);
268 if(!value)
270 g_log(NULL, G_LOG_LEVEL_DEBUG, "Overview page of the protocol '%s' is not defined", proto_abbrev);
271 continue;
275 * XXX - We could save some memory here if we stored the location template
276 * and search value as separate items. However, that makes freeing them
277 * a bit messy.
279 loc = string_replace(phkf->loc_template, PH_PATH_SEARCH_STR, value);
280 g_free(value);
281 if (!loc || !strlen(loc)) continue;
283 if (add_separator) {
284 menu_entry = g_malloc0(sizeof(GtkItemFactoryEntry));
285 menu_entry->path = g_strdup_printf("%s/<separator>", PH_MENU_TOP);
286 menu_entry->item_type = "<Separator>";
287 gtk_item_factory_create_item(g_ph_menu_factory, menu_entry, NULL, 2);
289 add_separator = TRUE;
291 menu_entry = g_malloc0(sizeof(GtkItemFactoryEntry));
292 menu_entry->path = g_strdup_printf("%s/%s %s Overview", PH_MENU_TOP, phkf->source, proto_name);
293 menu_entry->callback = ph_menu_onclick;
294 gtk_item_factory_create_item(g_ph_menu_factory, menu_entry, loc, 2);
295 menu_item = gtk_item_factory_get_widget(g_ph_menu_factory, menu_entry->path);
296 g_assert(menu_item);
297 g_signal_connect(menu_item, "destroy", G_CALLBACK(url_destroy_cb), loc);
299 found = TRUE;
301 if(description)
303 keys = ph_ini_get_keywords(phkf->keyfile, proto_abbrev);
304 table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
306 for(i = 0; keys[i] != NULL; i++)
308 if(!strcmp(keys[i], PH_INI_KEY_OVERVIEW)) continue; /* We already added the overview */
309 if(!ph_parse_string(description, keys[i])) continue; /* Bad format */
310 if(g_hash_table_lookup(table, g_ascii_strup(keys[i], -1)) != NULL) continue; /* Duplicate */
312 value = ph_ini_get_path(phkf->keyfile, proto_abbrev, keys[i]);
313 if(!value || !strlen(value)) continue;
315 loc = string_replace(phkf->loc_template, PH_PATH_SEARCH_STR, value);
316 g_free(value);
317 if (!loc || !strlen(loc)) continue;
319 g_hash_table_insert(table, g_ascii_strup(keys[i], -1), GINT_TO_POINTER(1));
321 menu_entry = g_malloc0(sizeof(GtkItemFactoryEntry));
322 menu_entry->path = g_strdup_printf("%s/%s", PH_MENU_TOP, keys[i]);
323 menu_entry->callback = ph_menu_onclick;
324 gtk_item_factory_create_item(g_ph_menu_factory, menu_entry, loc, 2);
325 menu_item = gtk_item_factory_get_widget(g_ph_menu_factory, menu_entry->path);
326 g_assert(menu_item);
327 g_signal_connect(menu_item, "destroy", G_CALLBACK(url_destroy_cb), loc);
330 g_hash_table_destroy(table);
334 g_free(description);
335 menu_item = gtk_item_factory_get_item(g_ph_menu_factory, PH_MENU_TOP);
336 gtk_widget_set_sensitive(menu_item, found);
340 * Function ph_menu_onclick
342 * @param GtkWidget *widget Description
343 * @param gpointer data Description
344 * @param guint action Description
345 * @return void
347 static void ph_menu_onclick(GtkWidget *widget _U_, gpointer data, guint action _U_)
349 gchar *loc = (gchar *) data;
351 g_log(NULL, G_LOG_LEVEL_DEBUG, "Sending '%s' to the browser.", loc);
353 if (! loc) {
354 g_log(NULL, G_LOG_LEVEL_DEBUG, "Protocol help ended up with a NULL URL.");
355 return;
358 /* XXX - Should we do any RFC 3986 escaping first? */
359 if (! browser_open_url(loc)) {
360 g_log(NULL, G_LOG_LEVEL_DEBUG, "Couldn't get protocol help for %s", loc);
364 /** Get the field ID for a selected tree item.
366 * @param selection Tree selection
367 * @param cfile Capture file
368 * @return Field ID or 0
370 static int
371 ph_capture_get_protocol_id(GtkTreeSelection *selection, capture_file *cf)
373 GtkTreeModel *model = NULL;
374 GtkTreeIter iter;
375 GtkTreeIter parent;
376 field_info *finfo = NULL;
377 int proto_id = 0;
379 if(!cf->finfo_selected) return 0;
380 proto_id = cf->finfo_selected->hfinfo->id;
382 if(!proto_id)
384 if(!gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection), &model, &iter)) return 0;
386 while(gtk_tree_model_iter_parent(model, &parent, &iter))
388 gtk_tree_model_get(model, &parent, 1, &finfo, -1);
389 iter = parent;
391 if(finfo->hfinfo->id > 0)
393 proto_id = finfo->hfinfo->id;
394 break;
399 while(proto_id && !proto_registrar_is_protocol(proto_id))
401 proto_id = proto_registrar_get_parent(proto_id);
404 return proto_id;
407 /** Get the protocol name for a selected tree item.
409 * @param selection Tree selection
410 * @param cfile Capture file
411 * @return Name for a protocol or NULL
413 static const gchar*
414 ph_capture_get_protocol_name(GtkTreeSelection *selection, capture_file *cf)
416 int proto_id = ph_capture_get_protocol_id(selection, cf);
418 return (!proto_id) ? NULL : proto_get_protocol_short_name(find_protocol_by_id(proto_id));
421 /** Get the abbreviated protocol name for a selected tree item.
423 * @param selection Tree selection
424 * @param cfile Capture file
425 * @return Abbreviated (lower-case) name for a protocol or NULL
427 static const gchar*
428 ph_capture_get_protocol_abbrev(GtkTreeSelection *selection, capture_file *cf)
430 int proto_id = ph_capture_get_protocol_id(selection, cf);
432 return (!proto_id) ? NULL : proto_registrar_get_abbrev(proto_id);
435 /** Return the selected item description
436 * @param cf capture file pointer
437 * @return Description of the selected item. MUST be freed with g_free().
439 static gchar* ph_capture_get_description(capture_file *cf)
441 gchar *buffer = NULL;
443 if(cf->finfo_selected->rep &&
444 strlen(cf->finfo_selected->rep->representation) > 0)
446 buffer = g_strdup(cf->finfo_selected->rep->representation);
448 else
450 buffer = g_malloc(ITEM_LABEL_LENGTH);
451 proto_item_fill_label(cf->finfo_selected, buffer);
454 return buffer;
457 /** Load a protocol help key file and add it to the global array.
459 * @param filename Full path to the key file.
460 * @return Newly-created key file entry or NULL.
462 static proto_help_key_file *
463 ph_ini_load_file(const gchar *filename)
465 GKeyFile *kf;
466 GError *error = NULL;
467 proto_help_key_file *phkf = NULL;
468 gchar *old_template, *loc_template;
469 gchar **loc_data, *loc_repl, *loc_search;
470 gsize i, len;
472 if(!g_file_test(filename, G_FILE_TEST_EXISTS))
474 g_log(NULL, G_LOG_LEVEL_DEBUG, "Configuration file %s does not exists", filename);
475 return NULL;
478 kf = g_key_file_new();
480 if(!g_key_file_load_from_file(kf, filename, G_KEY_FILE_NONE, &error))
482 g_log(NULL, G_LOG_LEVEL_DEBUG, "Configuration file '%s' could not be loaded (%s)", filename, error->message);
483 g_error_free(error);
484 g_key_file_free(kf);
486 return NULL;
489 loc_template = ph_ini_get_value(kf, PH_INI_GROUP_DATABASE, PH_INI_DB_KEY_LOCATION_TEMPLATE, NULL);
490 if (!loc_template) {
491 g_key_file_free(kf);
492 return NULL;
495 loc_data = g_key_file_get_keys(kf, PH_INI_GROUP_LOCATION_DATA, &len, NULL);
496 if (loc_data) {
497 for (i = 0; i < len; i++) {
498 loc_repl = ph_ini_get_value(kf, PH_INI_GROUP_LOCATION_DATA, loc_data[i], NULL);
499 old_template = loc_template;
500 loc_search = g_strdup_printf("${%s}", loc_data[i]);
501 loc_template = string_replace(loc_template, loc_search, loc_repl);
502 g_free(loc_repl);
503 g_free(loc_search);
504 g_free(old_template);
508 /* Add ${PATH} to the end if it's not present */
509 if (!strstr(loc_template, PH_PATH_SEARCH_STR)) {
510 old_template = loc_template;
511 loc_template = g_strdup_printf("%s" PH_PATH_SEARCH_STR, old_template);
512 g_free(old_template);
515 phkf = g_malloc(sizeof(proto_help_key_file));
516 phkf->keyfile = kf;
517 phkf->source = ph_ini_get_value(kf, PH_INI_GROUP_DATABASE, PH_INI_DB_KEY_SOURCE, "(Unknown source)");
518 phkf->loc_template = loc_template;
520 g_ptr_array_add(g_ph_key_files, phkf);
522 return phkf;
525 /** Fetch a value for a key from a key file with an optional default value.
527 * @param keyfile The key file to search
528 * @param group Key file group
529 * @param key Key file value to fetch
530 * @param alt Alternate string to return. May be NULL.
531 * @return const gchar* A newly-allocated key value, or a copy of alt if not found.
533 static gchar*
534 ph_ini_get_value(GKeyFile *keyfile, const gchar *group, const gchar *key, gchar *alt)
536 gchar *value = NULL;
538 if (keyfile) {
539 value = g_key_file_get_string(keyfile, group, key, NULL);
542 if (!value) {
543 value = g_strdup(alt);
546 return value;
549 /** Given a protocol name and a key, map the protocol name to a section in the
550 * keyfile, then look up the value for that key.
552 * @param keyfile The key file to search
553 * @param protocol Wireshark protocol name to map, e.g. "tcp".
554 * @param keyword The key to fetch from the mapped section.
555 * @return
557 static gchar*
558 ph_ini_get_path(GKeyFile *keyfile, const gchar *protocol, const gchar *keyword)
560 GError *error = NULL;
561 gchar *map;
562 gchar *value;
564 if(!keyfile || !protocol || !keyword) return NULL;
566 map = g_key_file_get_string(keyfile, PH_INI_GROUP_MAP, protocol, &error);
568 if(!map)
570 g_log(NULL, G_LOG_LEVEL_DEBUG, "Protocol '%s' is not defined (%s)", protocol, error->message);
571 g_error_free(error);
572 return NULL;
575 value = g_key_file_get_string(keyfile, map, keyword, NULL);
576 g_free(map);
577 return value;
580 /** Given a protocol name, map it to a section in the keyfile, then
581 * return the keys in that section.
583 * @param keyfile The key file to search
584 * @param protocol Wireshark protocol name to map, e.g. "tcp".
585 * @return An array of keys in the mapped section. Must be freed with g_strfreev().
587 static gchar**
588 ph_ini_get_keywords(GKeyFile *keyfile, const gchar *protocol)
590 GError *error = NULL;
591 gchar *map;
592 gchar **keys;
593 gsize length = 0;
595 if(!keyfile) return NULL;
597 map = g_key_file_get_string(keyfile, PH_INI_GROUP_MAP, protocol, &error);
599 if(!map)
601 g_log(NULL, G_LOG_LEVEL_DEBUG, "Protocol '%s' is not defined (%s)", protocol, error->message);
602 g_error_free(error);
603 return NULL;
606 error = NULL;
607 keys = g_key_file_get_keys(keyfile, map, &length, &error);
608 g_free(map);
610 if(!keys)
612 g_log(NULL, G_LOG_LEVEL_DEBUG, "Display titles are not defined (%s)", protocol);
613 g_error_free(error);
616 return keys;
620 * Function ph_parse_string
622 * @param const gchar *description Description
623 * @param const gchar *value Description
624 * @return guint Description
626 static guint ph_parse_string(const gchar *description, const gchar *value)
628 GRegex *regex = NULL;
629 GMatchInfo *match_info = NULL;
630 gchar *pattern = NULL;
631 guint result = 0;
633 pattern = g_strdup_printf("(?<![0-9a-zA-Z_])%s(?![0-9a-zA-Z_])", value);
634 regex = g_regex_new(pattern, 0, 0, NULL);
636 g_regex_match(regex, description, 0, &match_info);
637 if(g_match_info_matches(match_info)) result = 1;
639 g_match_info_free(match_info);
640 g_regex_unref(regex);
642 return result;
645 #ifdef PH_DEBUG_LOG
647 * Function ph_logging_handler
649 * @param const gchar *domain Description
650 * @param GLogLevelFlags level Description
651 * @param const gchar *message Description
652 * @param gpointer data Description
653 * @return void
655 static void ph_logging_handler(const gchar *domain _U_, GLogLevelFlags level, const gchar *message, gpointer data _U_)
657 gchar *log = NULL;
658 gchar *type = NULL;
659 FILE *file = NULL;
660 struct tm *timestamp = NULL;
661 time_t now;
663 time(&now);
664 timestamp = localtime(&now);
666 switch(level & G_LOG_LEVEL_MASK)
668 case G_LOG_LEVEL_ERROR:
669 type = "ERR";
670 break;
672 case G_LOG_LEVEL_DEBUG:
673 type = "WARNING";
674 break;
676 case G_LOG_LEVEL_INFO:
677 type = "INFO";
678 break;
680 default:
681 type = "OTHER";
684 file = ws_fopen(ph_log_path, "a+");
686 if(file)
688 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);
689 fputs(log, file);
690 fclose(file);
693 #endif /* PH_DEBUG_LOG */
694 #endif /* MAIN_MENU_USE_UIMANAGER */