diff --git a/epan/funnel.c b/epan/funnel.c index 7308b34fc1..e3aaac2de3 100644 --- a/epan/funnel.c +++ b/epan/funnel.c @@ -34,6 +34,36 @@ static funnel_menu_t* added_menus = NULL; static funnel_menu_t* removed_menus = NULL; static gboolean menus_registered = FALSE; +/** + * Represents a single packet menu entry and callback + */ +typedef struct _funnel_packet_menu_t { + char *name; /**< Name to display in the GUI */ + char *required_fields; /**< comma-separated list of fields + that must be present for the + packet menu to be displayed */ + funnel_packet_menu_callback callback; /**< Lua function to be called on + menu item selection. */ + gpointer callback_data; /**< Lua state for the callback + function */ + gboolean retap; /**< Whether or not to rescan the + capture file's packets */ + struct _funnel_packet_menu_t* next; /**< Pointer to the next + _funnel_packet_menu_t for the + singly-linked list + implemenation */ +} funnel_packet_menu_t; + +/* + * List of all registered funnel_packet_menu_t's + */ +static funnel_packet_menu_t* registered_packet_menus = NULL; +/* + * TRUE if the packet menus were modified since the last registration + */ +static gboolean packet_menus_modified = FALSE; +static void funnel_clear_packet_menu (funnel_packet_menu_t** menu_list); + const funnel_ops_t* funnel_get_funnel_ops(void) { return ops; } void funnel_set_funnel_ops(const funnel_ops_t* o) { ops = o; } @@ -120,6 +150,10 @@ void funnel_deregister_menus(funnel_menu_callback callback) funnel_remove_menu(®istered_menus, m); funnel_insert_menu(&removed_menus, m); + + // Clear and free memory of packet menus + funnel_clear_packet_menu(®istered_packet_menus); + packet_menus_modified = TRUE; } void funnel_register_all_menus(funnel_registration_cb_t r_cb) @@ -146,9 +180,103 @@ void funnel_reload_menus(funnel_deregistration_cb_t d_cb, funnel_clear_menu(&added_menus); } + +/* + * Inserts a funnel_packet_menu_t into a list of funnel_packet_menu_t's + * + * @param menu_list the list of menus that the menu will be added to + * @param menu the menu to add to the list of menus + */ +static void funnel_insert_packet_menu (funnel_packet_menu_t** menu_list, funnel_packet_menu_t *menu) +{ + if (!(*menu_list)) { + *menu_list = menu; + } else { + funnel_packet_menu_t* c; + for (c = *menu_list; c->next; c = c->next); + c->next = menu; + } +} + +/** + * Entry point for Lua code to register a packet menu + * + * Stores the menu name and callback from the Lua code + * into registered_packet_menus so that the + * Wireshark GUI code can retrieve it with + * funnel_register_all_packet_menus(). + */ +void funnel_register_packet_menu(const char *name, + const char *required_fields, + funnel_packet_menu_callback callback, + gpointer callback_data, + gboolean retap) +{ + funnel_packet_menu_t* m = g_new0(funnel_packet_menu_t, 1); + m->name = g_strdup(name); + m->required_fields = g_strdup(required_fields); + m->callback = callback; + m->callback_data = callback_data; + m->retap = retap; + m->next = NULL; + + funnel_insert_packet_menu(®istered_packet_menus, m); + packet_menus_modified = TRUE; +} + +/** + * Clears a list of funnel_packet_menu_t's and free()s all associated memory + * + * @param menu_list the list of menus to clear + */ +static void funnel_clear_packet_menu (funnel_packet_menu_t** menu_list) +{ + funnel_packet_menu_t *m; + + while (*menu_list) { + m = *menu_list; + *menu_list = m->next; + g_free(m->name); + g_free(m->required_fields); + if (m->callback_data) { + g_free(m->callback_data); + } + g_free(m); + } + *menu_list = NULL; +} + +/** + * Entry point for Wireshark GUI to obtain all registered packet menus + * + * Calls the supplied callback for each packet menu registered with + * funnel_register_packet_menu(). + * + * @param r_cb the callback function to call with each registered packet menu + */ +void funnel_register_all_packet_menus(funnel_registration_packet_cb_t r_cb) +{ + funnel_packet_menu_t* c; + for (c = registered_packet_menus; c; c = c->next) { + r_cb(c->name,c->required_fields,c->callback,c->callback_data,c->retap); + } + packet_menus_modified = FALSE; +} + +/** + * Returns whether the packet menus have been modified since they were last registered + * + * @return TRUE if the packet menus were modified since the last registration + */ +gboolean funnel_packet_menus_modified(void) +{ + return packet_menus_modified; +} + void funnel_cleanup(void) { funnel_clear_menu(®istered_menus); + funnel_clear_packet_menu(®istered_packet_menus); } /* diff --git a/epan/funnel.h b/epan/funnel.h index 02096d5159..e817af85d6 100644 --- a/epan/funnel.h +++ b/epan/funnel.h @@ -118,6 +118,49 @@ WS_DLL_PUBLIC void funnel_reload_menus(funnel_deregistration_cb_t d_cb, funnel_registration_cb_t r_cb); WS_DLL_PUBLIC void funnel_cleanup(void); +/** + * Signature of function that can be called from a custom packet menu entry + */ +typedef void (* funnel_packet_menu_callback)(gpointer, GPtrArray*); + +/** + * Signature of callback function to register packet menu entries + */ +typedef void (*funnel_registration_packet_cb_t)(const char *name, + const char *required_fields, + funnel_packet_menu_callback callback, + gpointer callback_data, + gboolean retap); + +/** + * Entry point for Wireshark GUI to obtain all registered packet menus + * + * @param r_cb function which will be called to register each packet menu entry + */ +WS_DLL_PUBLIC void funnel_register_all_packet_menus(funnel_registration_packet_cb_t r_cb); + +/** + * Entry point for Lua code to register a packet menu + * + * @param name packet menu item's name + * @param required_fields fields required to be present for the packet menu to be displayed + * @param callback function called when the menu item is invoked. The function must take one argument and return nothing. + * @param callback_data Lua state for the callback function + * @param retap whether or not to rescan all packets + */ +WS_DLL_PUBLIC void funnel_register_packet_menu(const char *name, + const char *required_fields, + funnel_packet_menu_callback callback, + gpointer callback_data, + gboolean retap); + +/** + * Returns whether the packet menus have been modified since they were last registered + * + * @return TRUE if the packet menus were modified since the last registration + */ +WS_DLL_PUBLIC gboolean funnel_packet_menus_modified(void); + extern void initialize_funnel_ops(void); extern void funnel_dump_all_text_windows(void); diff --git a/epan/wslua/wslua_gui.c b/epan/wslua/wslua_gui.c index 8a08c04552..6b2d368c60 100644 --- a/epan/wslua/wslua_gui.c +++ b/epan/wslua/wslua_gui.c @@ -137,6 +137,93 @@ void wslua_deregister_menus(void) { funnel_deregister_menus(lua_menu_callback); } +/** + * Error handler used by lua_custom_packet_menu_callback when calling the user-supplied callback + * + * @param L State of the Lua interpreter + * @return Always returns 0 + */ +static int packet_menu_cb_error_handler(lua_State* L) { + const gchar* error = lua_tostring(L,1); + report_failure("Lua: Error During execution of Packet Menu Callback:\n %s",error); + return 0; +} + +/** + * Wrapper used to call the user-supplied Lua callback when a custom packet + * context menu is clicked. + * + * @param data Lua menu data + * @param finfo_array packet data + */ +static void lua_custom_packet_menu_callback(gpointer data, GPtrArray *finfo_array) { + // _lua_menu_data is State + the integer index of a callback. + struct _lua_menu_data* md = (struct _lua_menu_data *)data; + lua_State* L = md->L; + + lua_settop(L,0); + lua_pushcfunction(L,packet_menu_cb_error_handler); + lua_rawgeti(L, LUA_REGISTRYINDEX, md->cb_ref); + + // Push the packet data as arguments to the Lua callback: + int items_found = 0; + for (guint i = finfo_array->len - 1; i > 0 ; i --) { + field_info *fi = (field_info *)g_ptr_array_index (finfo_array, i); + push_FieldInfo(L, fi); + items_found++; + } + + switch ( lua_pcall(L,items_found,0,1) ) { + case 0: + break; + case LUA_ERRRUN: + g_warning("Runtime error while calling custom_packet_menu callback"); + break; + case LUA_ERRMEM: + g_warning("Memory alloc error while calling custom_packet_menu callback"); + break; + default: + g_assert_not_reached(); + break; + } + + return; +} + +/** + * Lua function exposed to users: register_packet_menu + */ +WSLUA_FUNCTION wslua_register_packet_menu(lua_State* L) { /* Register a menu item in the packet list. */ +#define WSLUA_ARG_register_packet_menu_NAME 1 /* The name of the menu item. Use slashes to separate submenus. (e.g. level1/level2/name). (string) */ +#define WSLUA_ARG_register_packet_menu_ACTION 2 /* The function to be called when the menu item is invoked. The function must take one argument and return nothing. */ +#define WSLUA_OPTARG_register_packet_menu_REQUIRED_FIELDS 3 /* A comma-separated list of packet fields (e.g., http.host,dns.qry.name) which all must be present for the menu to be displayed (default: always display)*/ + + const gchar* name = luaL_checkstring(L,WSLUA_ARG_register_packet_menu_NAME); + const gchar* required_fields = luaL_optstring(L,WSLUA_OPTARG_register_packet_menu_REQUIRED_FIELDS,""); + + struct _lua_menu_data* md; + gboolean retap = FALSE; + + if (!lua_isfunction(L,WSLUA_ARG_register_packet_menu_ACTION)) { + WSLUA_ARG_ERROR(register_packet_menu,ACTION,"Must be a function"); + return 0; + } + + md = g_new0(struct _lua_menu_data, 1); + md->L = L; + + lua_pushvalue(L, 2); + md->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); + lua_remove(L,2); + + funnel_register_packet_menu(name, + required_fields, + lua_custom_packet_menu_callback, + md, + retap); + WSLUA_RETURN(0); +} + struct _dlg_cb_data { lua_State* L; int func_ref; diff --git a/packaging/debian/libwireshark0.symbols b/packaging/debian/libwireshark0.symbols index f0ce9a7350..6fff8a357f 100644 --- a/packaging/debian/libwireshark0.symbols +++ b/packaging/debian/libwireshark0.symbols @@ -748,8 +748,11 @@ libwireshark.so.0 libwireshark0 #MINVER# ftype_pretty_name@Base 1.9.1 funnel_cleanup@Base 2.9.1 funnel_get_funnel_ops@Base 1.9.1 + funnel_packet_menus_modified@Base 4.1.0 funnel_register_all_menus@Base 1.9.1 + funnel_register_all_packet_menus@Base 4.1.0 funnel_register_menu@Base 1.9.1 + funnel_register_packet_menu@Base 4.1.0 funnel_reload_menus@Base 1.99.9 funnel_set_funnel_ops@Base 1.9.1 fvalue_from_literal@Base 3.7.0 diff --git a/ui/qt/funnel_statistics.cpp b/ui/qt/funnel_statistics.cpp index 752858e862..ee8a34dc66 100644 --- a/ui/qt/funnel_statistics.cpp +++ b/ui/qt/funnel_statistics.cpp @@ -21,6 +21,7 @@ #include "ui/progress_dlg.h" #include "ui/simple_dialog.h" +#include #include "funnel_statistics.h" #include "funnel_string_dialog.h" @@ -30,6 +31,7 @@ #include #include #include +#include #include #include "main_application.h" @@ -61,48 +63,138 @@ static void progress_window_update(struct progdlg *progress_dialog, float percen static void progress_window_destroy(struct progdlg *progress_dialog); } -class FunnelAction : public QAction -{ -public: - FunnelAction(QString title, funnel_menu_callback callback, gpointer callback_data, gboolean retap, QObject *parent = nullptr) : +FunnelAction::FunnelAction(QString title, funnel_menu_callback callback, gpointer callback_data, gboolean retap, QObject *parent = nullptr) : QAction(parent), title_(title), callback_(callback), callback_data_(callback_data), retap_(retap) +{ + // Use "&&" to get a real ampersand in the menu item. + title.replace('&', "&&"); + + setText(title); + setObjectName(FunnelStatistics::actionName()); + packetRequiredFields_ = QSet(); +} + +FunnelAction::FunnelAction(QString title, funnel_packet_menu_callback callback, gpointer callback_data, gboolean retap, const char *packet_required_fields, QObject *parent = nullptr) : + QAction(parent), + title_(title), + callback_data_(callback_data), + retap_(retap), + packetCallback_(callback), + packetRequiredFields_(QSet()) +{ + // Use "&&" to get a real ampersand in the menu item. + title.replace('&', "&&"); + + QStringList menuComponents = title.split(QString("/")); + // Set the menu's text to the rightmost component, set the path to being everything to the left: + setText("(empty)"); + packetSubmenu_ = ""; + if (!menuComponents.isEmpty()) { - // Use "&&" to get a real ampersand in the menu item. - title.replace('&', "&&"); - - setText(title); - setObjectName(FunnelStatistics::actionName()); + setText(menuComponents.last()); + menuComponents.removeLast(); + packetSubmenu_ = menuComponents.join("/"); } - funnel_menu_callback callback() const { - return callback_; - } + setObjectName(FunnelStatistics::actionName()); + setPacketRequiredFields(packet_required_fields); +} - QString title() const { - return title_; - } +FunnelAction::~FunnelAction(){ +} - void triggerCallback() { - if (callback_) { - callback_(callback_data_); +funnel_menu_callback FunnelAction::callback() const { + return callback_; +} + +QString FunnelAction::title() const { + return title_; +} + +void FunnelAction::triggerCallback() { + if (callback_) { + callback_(callback_data_); + } +} + +void FunnelAction::setPacketCallback(funnel_packet_menu_callback packet_callback) { + packetCallback_ = packet_callback; +} + +void FunnelAction::setPacketRequiredFields(const char *required_fields_str) { + packetRequiredFields_.clear(); + // If multiple fields are required to be present, they're split by commas + // Also remove leading and trailing spaces, in case someone writes + // "http, dns" instead of "http,dns" + QString requiredFieldsJoined = QString(required_fields_str); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + QStringList requiredFieldsSplit = requiredFieldsJoined.split(",", Qt::SkipEmptyParts); +#else + QStringList requiredFieldsSplit = requiredFieldsJoined.split(",", QString::SkipEmptyParts); +#endif + foreach (QString requiredField, requiredFieldsSplit) { + QString trimmedFieldName = requiredField.trimmed(); + if (! trimmedFieldName.isEmpty()) { + packetRequiredFields_.insert(trimmedFieldName); } } +} - bool retap() { - if (retap_) return true; - return false; +const QSet FunnelAction::getPacketRequiredFields() { + return packetRequiredFields_; +} + + +void FunnelAction::setPacketData(GPtrArray* finfos) { + packetData_ = finfos; +} + +void FunnelAction::addToMenu(QMenu * ctx_menu, QHash menuTextToMenus) { + QString submenusText = this->getPacketSubmenus(); + if (submenusText.isEmpty()) { + ctx_menu->addAction(this); + } else { + // If the action has a submenu, ensure that the + // the full submenu chain exists: + QStringList menuComponents = submenusText.split("/"); + QString menuSubComponentsStringPrior = NULL; + for (int menuIndex=0; menuIndex < menuComponents.size(); menuIndex++) { + QStringList menuSubComponents = menuComponents.mid(0, menuIndex+1); + QString menuSubComponentsString = menuSubComponents.join("/"); + if (!menuTextToMenus.contains(menuSubComponentsString)) { + // Create a new menu object under the prior object + QMenu *previousSubmenu = menuTextToMenus.value(menuSubComponentsStringPrior); + QMenu *submenu = previousSubmenu->addMenu(menuComponents.at(menuIndex)); + menuTextToMenus.insert(menuSubComponentsString, submenu); + } + menuSubComponentsStringPrior = menuSubComponentsString; + } + // Then add the action to the relevant submenu + QMenu *parentMenu = menuTextToMenus.value(submenusText); + parentMenu->addAction(this); } -private: - QString title_; - funnel_menu_callback callback_; - gpointer callback_data_; - gboolean retap_; -}; +} + + +void FunnelAction::triggerPacketCallback() { + if (packetCallback_) { + packetCallback_(callback_data_, packetData_); + } +} + +bool FunnelAction::retap() { + if (retap_) return true; + return false; +} + +QString FunnelAction::getPacketSubmenus() { + return packetSubmenu_; +} static QHash > funnel_actions_; const QString FunnelStatistics::action_name_ = "FunnelStatisticsAction"; @@ -368,6 +460,33 @@ static void register_menu_cb(const char *name, funnel_actions_[group] << funnel_action; } +/* + * Callback used to register packet menus in the GUI. + * + * Creates a new FunnelAction with the Lua + * callback and stores it in the Wireshark GUI with + * appendPacketMenu() so it can be retrieved when + * the packet's context menu is open. + * + * @param name packet menu item's name + * @param required_fields fields required to be present for the packet menu to be displayed + * @param callback function called when the menu item is invoked. The function must take one argument and return nothing. + * @param callback_data Lua state for the callback function + * @param retap whether or not to rescan all packets + */ +static void register_packet_menu_cb(const char *name, + const char *required_fields, + funnel_packet_menu_callback callback, + gpointer callback_data, + gboolean retap) +{ + FunnelAction *funnel_action = new FunnelAction(name, callback, callback_data, retap, required_fields, mainApp); + MainWindow * mainwindow = qobject_cast(mainApp->mainWindow()); + if (mainwindow) { + mainwindow->appendPacketMenu(funnel_action); + } +} + static void deregister_menu_cb(funnel_menu_callback callback) { foreach (int group, funnel_actions_.keys()) { @@ -397,6 +516,29 @@ void funnel_statistics_reload_menus(void) { funnel_reload_menus(deregister_menu_cb, register_menu_cb); + funnel_statistics_load_packet_menus(); +} + + +/** + * Returns whether the packet menus have been modified since they were last registered + * + * @return TRUE if the packet menus were modified since the last registration + */ +gboolean +funnel_statistics_packet_menus_modified(void) +{ + return funnel_packet_menus_modified(); +} + +/* + * Loads all registered_packet_menus into the + * Wireshark GUI. + */ +void +funnel_statistics_load_packet_menus(void) +{ + funnel_register_all_packet_menus(register_packet_menu_cb); } } // extern "C" diff --git a/ui/qt/funnel_statistics.h b/ui/qt/funnel_statistics.h index 4a2ec0d8d5..12e8cb44d6 100644 --- a/ui/qt/funnel_statistics.h +++ b/ui/qt/funnel_statistics.h @@ -11,13 +11,21 @@ #define FUNNELSTATISTICS_H #include +#include +#include +#include #include "capture_file.h" #include struct _funnel_ops_t; struct progdlg; +/** + * Signature of function that can be called from a custom packet menu entry + */ +typedef void (* funnel_packet_menu_callback)(gpointer, GPtrArray*); + class FunnelStatistics : public QObject { Q_OBJECT @@ -53,8 +61,42 @@ private: QString prepared_filter_; }; +class FunnelAction : public QAction +{ + Q_OBJECT +public: + FunnelAction(QString title, funnel_menu_callback callback, gpointer callback_data, gboolean retap, QObject *parent); + FunnelAction(QString title, funnel_packet_menu_callback callback, gpointer callback_data, gboolean retap, const char *packet_required_fields, QObject *parent); + ~FunnelAction(); + funnel_menu_callback callback() const; + QString title() const; + void triggerCallback(); + void setPacketCallback(funnel_packet_menu_callback packet_callback); + void setPacketData(GPtrArray* finfos); + void addToMenu(QMenu * ctx_menu, QHash menuTextToMenus); + void setPacketRequiredFields(const char *required_fields_str); + const QSet getPacketRequiredFields(); + bool retap(); + QString getPacketSubmenus(); + +public slots: + void triggerPacketCallback(); + +private: + QString title_; + QString packetSubmenu_; + funnel_menu_callback callback_; + gpointer callback_data_; + gboolean retap_; + funnel_packet_menu_callback packetCallback_; + GPtrArray* packetData_; + QSet packetRequiredFields_; +}; + extern "C" { void funnel_statistics_reload_menus(void); + void funnel_statistics_load_packet_menus(void); + gboolean funnel_statistics_packet_menus_modified(void); } // extern "C" #endif // FUNNELSTATISTICS_H diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp index bce8b53f63..7fcf479193 100644 --- a/ui/qt/main_window.cpp +++ b/ui/qt/main_window.cpp @@ -15,9 +15,13 @@ #include "main_window.h" +#include "funnel_statistics.h" #include "packet_list.h" #include "widgets/display_filter_combo.h" +// Packet Menu actions +static QList dynamic_packet_menu_actions; + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), main_stack_(nullptr), @@ -33,6 +37,11 @@ MainWindow::MainWindow(QWidget *parent) : } +MainWindow::~MainWindow() +{ + clearAddedPacketMenus(); +} + bool MainWindow::hasSelection() { if (packet_list_) @@ -89,4 +98,96 @@ void MainWindow::setDisplayFilter(QString filter, FilterAction::Action action, F emit filterAction(filter, action, filterType); } +/* + * Used for registering custom packet menus + * + * @param funnel_action a custom packet menu action + */ +void MainWindow::appendPacketMenu(QAction* funnel_action) +{ + dynamic_packet_menu_actions.append(funnel_action); + connect(funnel_action, SIGNAL(triggered(bool)), funnel_action, SLOT(triggerPacketCallback())); +} +/* + * Returns the list of registered packet menu actions + * + * After ensuring that all stored custom packet menu actions + * are registered with the Wireshark GUI, it returns them as a list + * so that they can potentially be displayed to a user. + * + * @return the list of registered packet menu actions + */ +QList MainWindow::getPacketMenuActions() +{ + if (funnel_statistics_packet_menus_modified()) { + // If the packet menus were modified, we need to clear the already + // loaded packet menus to avoid duplicates + this->clearAddedPacketMenus(); + funnel_statistics_load_packet_menus(); + } + return dynamic_packet_menu_actions; +} + +/* + * Clears the list of registered packet menu actions + * + * Clears the list of registered packet menu actions + * and frees all associated memory. + */ +void MainWindow::clearAddedPacketMenus() +{ + for( int i=0; i myPacketMenuActions = this->getPacketMenuActions(); + if (myPacketMenuActions.isEmpty()) { + return insertedPacketMenu; + } + + // Build a set of fields present for efficient lookups + QSet fieldsPresent = QSet(); + for (guint fieldInfoIndex = 0; fieldInfoIndex < finfo_array->len; fieldInfoIndex++) { + field_info *fi = (field_info *)g_ptr_array_index (finfo_array, fieldInfoIndex); + fieldsPresent.insert(QString(fi->hfinfo->abbrev)); + } + + // Place actions in the relevant (sub)menu + // The 'root' menu is the ctx_menu, so map NULL to that + QHash menuTextToMenus; + menuTextToMenus.insert(NULL, ctx_menu); + foreach (QAction * action, myPacketMenuActions) { + if (! qobject_cast(action)) { + continue; + } + FunnelAction * packetAction = qobject_cast(action); + + // Only display a menu if all required fields are present + if (!fieldsPresent.contains(packetAction->getPacketRequiredFields())) { + continue; + } + + packetAction->setPacketData(finfo_array); + packetAction->addToMenu(ctx_menu, menuTextToMenus); + insertedPacketMenu = true; + } + return insertedPacketMenu; +} diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index d1bc292165..233bfe1917 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -41,6 +41,7 @@ class MainWindow : public QMainWindow Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); bool hasSelection(); QList selectedRows(bool useFrameNum = false); void insertColumn(QString name, QString abbrev, gint pos = -1); @@ -50,6 +51,12 @@ public: QString getFilter(); MainStatusBar *statusBar(); + // Used for managing custom packet menus + void appendPacketMenu(QAction* funnel_action); + QList getPacketMenuActions(); + void clearAddedPacketMenus(); + bool addPacketMenus(QMenu * ctx_menu, GPtrArray *finfo_array); + public slots: void setDisplayFilter(QString filter, FilterAction::Action action, FilterAction::ActionType filterType); virtual void filterPackets(QString, bool) = 0; diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp index 138a29ba5e..2f35683609 100644 --- a/ui/qt/packet_list.cpp +++ b/ui/qt/packet_list.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -206,7 +207,8 @@ PacketList::PacketList(QWidget *parent) : set_column_visibility_(false), frozen_rows_(QModelIndexList()), cur_history_(-1), - in_history_(false) + in_history_(false), + finfo_array(NULL) { setItemsExpandable(false); setRootIsDecorated(false); @@ -263,6 +265,14 @@ PacketList::PacketList(QWidget *parent) : connect(verticalScrollBar(), SIGNAL(actionTriggered(int)), this, SLOT(vScrollBarActionTriggered(int))); } +PacketList::~PacketList() +{ + if (finfo_array) + { + g_ptr_array_free(finfo_array, TRUE); + } +} + void PacketList::colorsChanged() { const QString c_active = "active"; @@ -569,8 +579,13 @@ void PacketList::contextMenuEvent(QContextMenuEvent *event) proto_prefs_menus_.clear(); + if (finfo_array) + { + g_ptr_array_free(finfo_array, TRUE); + finfo_array = NULL; + } if (cap_file_ && cap_file_->edt && cap_file_->edt->tree) { - GPtrArray *finfo_array = proto_all_finfos(cap_file_->edt->tree); + finfo_array = proto_all_finfos(cap_file_->edt->tree); QList added_proto_prefs; for (guint i = 0; i < finfo_array->len; i++) { @@ -599,7 +614,6 @@ void PacketList::contextMenuEvent(QContextMenuEvent *event) added_proto_prefs << module_name; } } - g_ptr_array_free(finfo_array, TRUE); } QModelIndex ctxIndex = indexAt(event->pos()); @@ -623,6 +637,15 @@ void PacketList::contextMenuEvent(QContextMenuEvent *event) ctx_menu->addSeparator(); + // Code for custom context menus from Lua's register_packet_menu() + MainWindow * mainWindow = qobject_cast(mainApp->mainWindow()); + if (cap_file_ && cap_file_->edt && cap_file_->edt->tree && mainWindow) { + bool insertedPacketMenu = mainWindow->addPacketMenus(ctx_menu, finfo_array); + if (insertedPacketMenu) { + ctx_menu->addSeparator(); + } + } + ctx_menu->addAction(window()->findChild("actionViewEditResolvedName")); ctx_menu->addSeparator(); diff --git a/ui/qt/packet_list.h b/ui/qt/packet_list.h index 0a2c453a0a..08f0e40dda 100644 --- a/ui/qt/packet_list.h +++ b/ui/qt/packet_list.h @@ -40,6 +40,7 @@ class PacketList : public QTreeView Q_OBJECT public: explicit PacketList(QWidget *parent = 0); + ~PacketList(); enum SummaryCopyType { CopyAsText, @@ -142,6 +143,7 @@ private: QVector selection_history_; int cur_history_; bool in_history_; + GPtrArray *finfo_array; // Packet data from the last selected packet entry void setFrameReftime(gboolean set, frame_data *fdata); void setColumnVisibility(); diff --git a/ui/qt/wireshark_main_window_slots.cpp b/ui/qt/wireshark_main_window_slots.cpp index d522057fc1..7917dc2582 100644 --- a/ui/qt/wireshark_main_window_slots.cpp +++ b/ui/qt/wireshark_main_window_slots.cpp @@ -1547,6 +1547,7 @@ void WiresharkMainWindow::reloadLuaPlugins() mainApp->setReloadingLua(true); wslua_reload_plugins(NULL, NULL); + this->clearAddedPacketMenus(); funnel_statistics_reload_menus(); reloadDynamicMenus(); closePacketDialogs();