Merged in default (pull request #480)

Fri, 22 Mar 2019 01:54:47 +0000

author
Gary Kramlich <grim@reaperworld.com>
date
Fri, 22 Mar 2019 01:54:47 +0000
changeset 39493
72d1afcb0a8c
parent 39489
6d6255863536 (diff)
parent 39492
83e50cb13644 (current diff)
child 39496
47ccfc3f8b8b

Merged in default (pull request #480)

Replace some g_snprintf

Approved-by: Gary Kramlich
Approved-by: Eion Robb

libpurple/protocols/silc/chat.c file | annotate | diff | comparison | revisions
--- a/finch/gntblist.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/finch/gntblist.c	Fri Mar 22 01:54:47 2019 +0000
@@ -24,6 +24,7 @@
 #include NCURSES_HEADER
 
 #include <account.h>
+#include <action.h>
 #include <buddylist.h>
 #include <log.h>
 #include <notify.h>
@@ -995,10 +996,10 @@
 	for(list = purple_protocol_client_iface_blist_node_menu(protocol, node); list;
 			list = g_list_delete_link(list, list))
 	{
-		PurpleMenuAction *act = (PurpleMenuAction *) list->data;
+		PurpleActionMenu *act = (PurpleActionMenu *) list->data;
 		if (!act)
 			continue;
-		purple_menu_action_set_data(act, node);
+		purple_action_menu_set_data(act, node);
 		finch_append_menu_action(menu, act, node);
 	}
 }
@@ -1007,7 +1008,7 @@
 add_custom_action(GntMenu *menu, const char *label, PurpleCallback callback,
 		gpointer data)
 {
-	PurpleMenuAction *action = purple_menu_action_new(label, callback, data, NULL);
+	PurpleActionMenu *action = purple_action_menu_new(label, callback, data, NULL);
 	finch_append_menu_action(menu, action, NULL);
 }
 
@@ -1085,23 +1086,23 @@
 static void
 autojoin_toggled(GntMenuItem *item, gpointer data)
 {
-	PurpleMenuAction *action = data;
-	purple_blist_node_set_bool(purple_menu_action_get_data(action), "gnt-autojoin",
+	PurpleActionMenu *action = data;
+	purple_blist_node_set_bool(purple_action_menu_get_data(action), "gnt-autojoin",
 				gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item)));
 }
 
 static void
 create_chat_menu(GntMenu *menu, PurpleChat *chat)
 {
-	PurpleMenuAction *action = purple_menu_action_new(_("Auto-join"), NULL, chat, NULL);
+	PurpleActionMenu *action = purple_action_menu_new(_("Auto-join"), NULL, chat, NULL);
 	GntMenuItem *check = gnt_menuitem_check_new(
-			purple_menu_action_get_label(action));
+			purple_action_menu_get_label(action));
 	gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(check),
 				purple_blist_node_get_bool((PurpleBlistNode*)chat, "gnt-autojoin"));
 	gnt_menu_add_item(menu, check);
 	gnt_menuitem_set_callback(check, autojoin_toggled, action);
 	g_signal_connect_swapped(G_OBJECT(menu), "destroy",
-			G_CALLBACK(purple_menu_action_free), action);
+			G_CALLBACK(purple_action_menu_free), action);
 
 	/* Protocol actions */
 	append_proto_menu(menu,
--- a/finch/gntconv.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/finch/gntconv.c	Fri Mar 22 01:54:47 2019 +0000
@@ -659,7 +659,7 @@
 
 	menu_actions = purple_e2ee_provider_get_conv_menu_actions(eprov, conv);
 	for (it = menu_actions; it; it = g_list_next(it)) {
-		PurpleMenuAction *action = it->data;
+		PurpleActionMenu *action = it->data;
 
 		finch_append_menu_action(sub, action, conv);
 	}
--- a/finch/gntmenuutil.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/finch/gntmenuutil.c	Fri Mar 22 01:54:47 2019 +0000
@@ -30,20 +30,20 @@
 static void
 context_menu_callback(GntMenuItem *item, gpointer data)
 {
-	PurpleMenuAction *action = data;
+	PurpleActionMenu *action = data;
 	if (action) {
 		void (*callback)(gpointer, gpointer);
 		callback = (void (*)(gpointer, gpointer))
-			purple_menu_action_get_callback(action);
+			purple_action_menu_get_callback(action);
 		if (callback) {
 			gpointer ctx = g_object_get_data(G_OBJECT(item), "menuctx");
-			callback(ctx, purple_menu_action_get_data(action));
+			callback(ctx, purple_action_menu_get_data(action));
 		}
 	}
 }
 
 void
-finch_append_menu_action(GntMenu *menu, PurpleMenuAction *action, gpointer ctx)
+finch_append_menu_action(GntMenu *menu, PurpleActionMenu *action, gpointer ctx)
 {
 	GList *list;
 	GntMenuItem *item;
@@ -52,7 +52,7 @@
 
 	if (action == NULL)
 		return;
-	label = purple_menu_action_get_label(action);
+	label = purple_action_menu_get_label(action);
 
 	if (strchr(label, '_') != NULL) {
 		clean_label = g_strdup(label);
@@ -62,22 +62,22 @@
 	item = gnt_menuitem_new(label);
 	g_free(clean_label);
 
-	if (purple_menu_action_get_callback(action)) {
+	if (purple_action_menu_get_callback(action)) {
 		gnt_menuitem_set_callback(item, context_menu_callback, action);
 		g_object_set_data(G_OBJECT(item), "menuctx", ctx);
 	}
 	gnt_menu_add_item(menu, item);
 
-	list = purple_menu_action_get_children(action);
+	list = purple_action_menu_get_children(action);
 
 	if (list) {
 		GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
 		gnt_menuitem_set_submenu(item, GNT_MENU(sub));
 		for (; list; list = g_list_delete_link(list, list))
 			finch_append_menu_action(GNT_MENU(sub), list->data, action);
-		purple_menu_action_set_children(action, NULL);
+		purple_action_menu_set_children(action, NULL);
 	}
 
 	g_signal_connect_swapped(G_OBJECT(menu), "destroy",
-		G_CALLBACK(purple_menu_action_free), action);
+		G_CALLBACK(purple_action_menu_free), action);
 }
--- a/finch/gntmenuutil.h	Wed Mar 20 19:06:31 2019 -0400
+++ b/finch/gntmenuutil.h	Fri Mar 22 01:54:47 2019 +0000
@@ -28,6 +28,8 @@
  * @title: Menu Utility functions
  */
 
+#include <action.h>
+
 #include <gnt.h>
 #include <gntmenu.h>
 
@@ -38,12 +40,12 @@
 /**
  * finch_append_menu_action:
  * @menu:   the GntMenu to add to
- * @action: the PurpleMenuAction to add
+ * @action: the PurpleActionMenu to add
  * @ctx:    the callback context, passed as the first argument to
- *               the PurpleMenuAction's PurpleCallback function.
+ *               the PurpleActionMenu's PurpleCallback function.
  *
- * Add a PurpleMenuAction to a GntMenu.
+ * Add a PurpleActionMenu to a GntMenu.
  */
-void finch_append_menu_action(GntMenu *menu, PurpleMenuAction *action, gpointer ctx);
+void finch_append_menu_action(GntMenu *menu, PurpleActionMenu *action, gpointer ctx);
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/action.c	Fri Mar 22 01:54:47 2019 +0000
@@ -0,0 +1,162 @@
+/* Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include "action.h"
+
+struct _PurpleActionMenu {
+	gchar *label;
+	GCallback callback;
+	gpointer data;
+	GList *children;
+	gchar *stock_icon;
+};
+
+/******************************************************************************
+ * ActionMenu API
+ *****************************************************************************/
+PurpleActionMenu *
+purple_action_menu_new(const gchar *label, GCallback callback, gpointer data,
+                       GList *children)
+{
+	PurpleActionMenu *act = g_new(PurpleActionMenu, 1);
+
+	act->label = g_strdup(label);
+	act->callback = callback;
+	act->data = data;
+	act->children = children;
+	act->stock_icon = NULL;
+
+	return act;
+}
+
+void
+purple_action_menu_free(PurpleActionMenu *act) {
+	g_return_if_fail(act != NULL);
+
+	purple_action_menu_set_children(act, NULL);
+
+	g_free(act->stock_icon);
+	g_free(act->label);
+	g_free(act);
+}
+
+const gchar *
+purple_action_menu_get_label(const PurpleActionMenu *act) {
+	g_return_val_if_fail(act != NULL, NULL);
+
+	return act->label;
+}
+
+GCallback
+purple_action_menu_get_callback(const PurpleActionMenu *act) {
+	g_return_val_if_fail(act != NULL, NULL);
+
+	return act->callback;
+}
+
+gpointer
+purple_action_menu_get_data(const PurpleActionMenu *act) {
+	g_return_val_if_fail(act != NULL, NULL);
+
+	return act->data;
+}
+
+GList *
+purple_action_menu_get_children(const PurpleActionMenu *act) {
+	g_return_val_if_fail(act != NULL, NULL);
+
+	return act->children;
+}
+
+void
+purple_action_menu_set_label(PurpleActionMenu *act, const gchar *label) {
+	g_return_if_fail(act != NULL);
+
+	g_free(act->label);
+
+	act->label = g_strdup(label);
+}
+
+void
+purple_action_menu_set_callback(PurpleActionMenu *act, GCallback callback) {
+	g_return_if_fail(act != NULL);
+
+	act->callback = callback;
+}
+
+void
+purple_action_menu_set_data(PurpleActionMenu *act, gpointer data) {
+	g_return_if_fail(act != NULL);
+
+	act->data = data;
+}
+
+void
+purple_action_menu_set_children(PurpleActionMenu *act, GList *children) {
+	g_return_if_fail(act != NULL);
+
+	g_list_free_full(act->children, purple_action_menu_free);
+
+	act->children = children;
+}
+
+const gchar *
+purple_action_menu_get_stock_icon(PurpleActionMenu *act) {
+	return act->stock_icon;
+}
+
+/******************************************************************************
+ * Protocol Action API
+ *****************************************************************************/
+PurpleProtocolAction *
+purple_protocol_action_new(const gchar* label, PurpleProtocolActionCallback callback) {
+	PurpleProtocolAction *action;
+
+	g_return_val_if_fail(label != NULL, NULL);
+	g_return_val_if_fail(callback != NULL, NULL);
+
+	action = g_new0(PurpleProtocolAction, 1);
+
+	action->label    = g_strdup(label);
+	action->callback = callback;
+
+	return action;
+}
+
+void
+purple_protocol_action_free(PurpleProtocolAction *action) {
+	g_return_if_fail(action != NULL);
+
+	g_free(action->label);
+	g_free(action);
+}
+
+PurpleProtocolAction *
+purple_protocol_action_copy(PurpleProtocolAction *action) {
+	g_return_val_if_fail(action != NULL, NULL);
+
+	return purple_protocol_action_new(action->label, action->callback);
+}
+
+G_DEFINE_BOXED_TYPE(
+	PurpleProtocolAction,
+	purple_protocol_action,
+	purple_protocol_action_copy,
+	purple_protocol_action_free
+);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/action.h	Fri Mar 22 01:54:47 2019 +0000
@@ -0,0 +1,219 @@
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef PURPLE_ACTION
+#define PURPLE_ACTION
+
+#include <glib.h>
+#include <glib-object.h>
+
+
+#define PURPLE_TYPE_PROTOCOL_ACTION  (purple_protocol_action_get_type())
+
+typedef struct _PurpleProtocolAction PurpleProtocolAction;
+
+typedef void (*PurpleProtocolActionCallback)(PurpleProtocolAction *action);
+
+/**
+ * PurpleActionMenu:
+ *
+ * A generic structure that contains information about an "action".  One
+ * place this is used is by protocols to tell the core the list of available
+ * right-click actions for a buddy list row.
+ */
+typedef struct _PurpleActionMenu PurpleActionMenu;
+
+#include "connection.h"
+
+/**
+ * PurpleProtocolAction:
+ *
+ * Represents an action that the protocol can perform. This shows up in the
+ * Accounts menu, under a submenu with the name of the account.
+ */
+struct _PurpleProtocolAction {
+	gchar *label;
+	PurpleProtocolActionCallback callback;
+	PurpleConnection *connection;
+	gpointer user_data;
+};
+
+G_BEGIN_DECLS
+
+/******************************************************************************
+ * Menu Action API
+ *****************************************************************************/
+
+/**
+ * purple_action_menu_new:
+ * @label:    The text label to display for this action.
+ * @callback: The function to be called when the action is used on
+ *                 the selected item.
+ * @data:     Additional data to be passed to the callback.
+ * @children: (element-type PurpleActionMenu) (transfer full): Menu actions to
+ *            be added as a submenu of this action.
+ *
+ * Creates a new PurpleActionMenu.
+ *
+ * Returns: The PurpleActionMenu.
+ */
+PurpleActionMenu *purple_action_menu_new(const gchar *label, GCallback callback, gpointer data, GList *children);
+
+/**
+ * purple_action_menu_free:
+ * @act: The PurpleActionMenu to free.
+ *
+ * Frees a PurpleActionMenu
+ */
+void purple_action_menu_free(PurpleActionMenu *act);
+
+/**
+ * purple_action_menu_get_label:
+ * @act:	The PurpleActionMenu.
+ *
+ * Returns the label of the PurpleActionMenu.
+ *
+ * Returns: The label string.
+ */
+const gchar *purple_action_menu_get_label(const PurpleActionMenu *act);
+
+/**
+ * purple_action_menu_get_callback:
+ * @act:	The PurpleActionMenu.
+ *
+ * Returns the callback of the PurpleActionMenu.
+ *
+ * Returns: The callback function.
+ */
+GCallback purple_action_menu_get_callback(const PurpleActionMenu *act);
+
+/**
+ * purple_action_menu_get_data:
+ * @act:	The PurpleActionMenu.
+ *
+ * Returns the data stored in the PurpleActionMenu.
+ *
+ * Returns: The data.
+ */
+gpointer purple_action_menu_get_data(const PurpleActionMenu *act);
+
+/**
+ * purple_action_menu_get_children:
+ * @act:	The PurpleActionMenu.
+ *
+ * Returns the children of the PurpleActionMenu.
+ *
+ * Returns: (element-type PurpleActionMenu) (transfer none): The menu children.
+ */
+GList* purple_action_menu_get_children(const PurpleActionMenu *act);
+
+/**
+ * purple_action_menu_set_label:
+ * @act:   The menu action.
+ * @label: The label for the menu action.
+ *
+ * Set the label to the PurpleActionMenu.
+ */
+void purple_action_menu_set_label(PurpleActionMenu *act, const gchar *label);
+
+/**
+ * purple_action_menu_set_callback:
+ * @act:        The menu action.
+ * @callback:   The callback.
+ *
+ * Set the callback that will be used by the PurpleActionMenu.
+ */
+void purple_action_menu_set_callback(PurpleActionMenu *act, GCallback callback);
+
+/**
+ * purple_action_menu_set_data:
+ * @act:   The menu action.
+ * @data:  The data used by this PurpleActionMenu
+ *
+ * Set the label to the PurpleActionMenu.
+ */
+void purple_action_menu_set_data(PurpleActionMenu *act, gpointer data);
+
+/**
+ * purple_action_menu_set_children:
+ * @act:       The menu action.
+ * @children: (element-type PurpleActionMenu) (transfer full): The menu children
+ *
+ * Set the children of the PurpleActionMenu.
+ */
+void purple_action_menu_set_children(PurpleActionMenu *act, GList *children);
+
+/**
+ * purple_action_menu_get_stock_icon:
+ * @act: The menu action.
+ *
+ * Gets the stock icon of the PurpleActionMenu.
+ *
+ * Returns: The stock icon identifier.
+ */
+const gchar *purple_action_menu_get_stock_icon(PurpleActionMenu *act);
+
+/******************************************************************************
+ * Protocol Action API
+ *****************************************************************************/
+
+/**
+ * purple_protocol_action_get_type:
+ *
+ * Returns: The #GType for the #PurpleProtocolAction boxed structure.
+ */
+GType purple_protocol_action_get_type(void);
+
+/**
+ * purple_protocol_action_new:
+ * @label:    The description of the action to show to the user.
+ * @callback: (scope call): The callback to call when the user selects this
+ *            action.
+ *
+ * Allocates and returns a new PurpleProtocolAction. Use this to add actions in
+ * a list in the get_actions function of the protocol.
+ *
+ * Returns: (transfer full): The new #PurpleProtocolAction.
+ */
+PurpleProtocolAction *purple_protocol_action_new(const gchar *label, PurpleProtocolActionCallback callback);
+
+/**
+ * purple_protocol_action_copy:
+ * @action: The #PurpleProtocolAction to copy.
+ *
+ * Creates a newly allocated copy of @action.
+ *
+ * Returns: (transfer full): A copy of @action.
+ */
+PurpleProtocolAction *purple_protocol_action_copy(PurpleProtocolAction *action);
+
+/**
+ * purple_protocol_action_free:
+ * @action: The PurpleProtocolAction to free.
+ *
+ * Frees a PurpleProtocolAction
+ */
+void purple_protocol_action_free(PurpleProtocolAction *action);
+
+
+G_END_DECLS
+
+#endif /* PURPLE_ACTION */
--- a/libpurple/blistnode.h	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/blistnode.h	Fri Mar 22 01:54:47 2019 +0000
@@ -289,7 +289,7 @@
  * purple_blist_node_get_extended_menu:
  * @n: The blist node for which to obtain the extended menu items.
  *
- * Returns: (element-type PurpleMenuAction): The extended menu items for a buddy
+ * Returns: (element-type PurpleActionMenu): The extended menu items for a buddy
  *          list node, as harvested by the blist-node-extended-menu signal.
  */
 GList *purple_blist_node_get_extended_menu(PurpleBlistNode *n);
--- a/libpurple/conversation.h	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/conversation.h	Fri Mar 22 01:54:47 2019 +0000
@@ -619,8 +619,8 @@
  *
  * Retrieves the extended menu items for the conversation.
  *
- * Returns: (element-type PurpleMenuAction):
- *          A list of PurpleMenuAction items, harvested by the
+ * Returns: (element-type PurpleActionMenu):
+ *          A list of PurpleActionMenu items, harvested by the
  *          chat-extended-menu signal. The list and the menuaction
  *          items should be freed by the caller.
  */
--- a/libpurple/e2ee.h	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/e2ee.h	Fri Mar 22 01:54:47 2019 +0000
@@ -223,7 +223,7 @@
  * The function is called, when user extends the E2EE menu for the conversation
  * specified in its parameter.
  *
- * Function should return the GList of PurpleMenuAction objects.
+ * Function should return the GList of PurpleActionMenu objects.
  */
 void
 purple_e2ee_provider_set_conv_menu_cb(PurpleE2eeProvider *provider,
@@ -245,7 +245,7 @@
  * @provider: The E2EE provider.
  * @conv: The conversation.
  *
- * Returns: (element-type PurpleMenuAction) (transfer full): The list of
+ * Returns: (element-type PurpleActionMenu) (transfer full): The list of
  *          actions for an E2EE menu.
  */
 GList *
--- a/libpurple/http.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/http.c	Fri Mar 22 01:54:47 2019 +0000
@@ -41,6 +41,8 @@
 
 #define PURPLE_HTTP_PROGRESS_WATCHER_DEFAULT_INTERVAL 250000
 
+#define PURPLE_HTTP_GET_ACCOUNT(gc) (gc ? purple_connection_get_account(gc) : NULL)
+
 typedef struct _PurpleHttpSocket PurpleHttpSocket;
 
 typedef struct _PurpleHttpHeaders PurpleHttpHeaders;
@@ -567,8 +569,7 @@
 	GSocketClient *client;
 	GError *error = NULL;
 
-	client = purple_gio_socket_client_new(
-			purple_connection_get_account(gc), &error);
+	client = purple_gio_socket_client_new(PURPLE_HTTP_GET_ACCOUNT(gc), &error);
 
 	if (client == NULL) {
 		purple_debug_error("http", "Error connecting to '%s:%d': %s",
@@ -869,8 +870,7 @@
 	req = hc->request;
 	url = hc->url;
 	hdrs = req->headers;
-	proxy = purple_proxy_get_setup(hc->gc ?
-		purple_connection_get_account(hc->gc) : NULL);
+	proxy = purple_proxy_get_setup(PURPLE_HTTP_GET_ACCOUNT(hc->gc));
 
 	proxy_http = (purple_proxy_info_get_proxy_type(proxy) == PURPLE_PROXY_HTTP ||
 		purple_proxy_info_get_proxy_type(proxy) == PURPLE_PROXY_USE_ENVVAR);
--- a/libpurple/meson.build	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/meson.build	Fri Mar 22 01:54:47 2019 +0000
@@ -2,6 +2,7 @@
 	'account.c',
 	'accounts.c',
 	'accountopt.c',
+	'action.c',
 	'attention.c',
 	'blistnode.c',
 	'buddy.c',
@@ -84,6 +85,7 @@
 	'account.h',
 	'accounts.h',
 	'accountopt.h',
+	'action.h',
 	'attention.h',
 	'blistnode.h',
 	'buddy.h',
--- a/libpurple/plugins/autoaccept.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/plugins/autoaccept.c	Fri Mar 22 01:54:47 2019 +0000
@@ -35,12 +35,12 @@
 #include <plugins.h>
 #include <version.h>
 
+#include <action.h>
 #include <buddylist.h>
 #include <conversation.h>
 #include <xfer.h>
 #include <request.h>
 #include <notify.h>
-#include <util.h>
 
 #define PREF_PREFIX		"/plugins/core/" PLUGIN_ID
 #define PREF_PATH		PREF_PREFIX "/path"
@@ -217,13 +217,13 @@
 static void
 context_menu(PurpleBlistNode *node, GList **menu, gpointer plugin)
 {
-	PurpleMenuAction *action;
+	PurpleActionMenu *action;
 
 	if (!PURPLE_IS_BUDDY(node) && !PURPLE_IS_CONTACT(node) &&
 		!purple_blist_node_is_transient(node))
 		return;
 
-	action = purple_menu_action_new(_("Autoaccept File Transfers..."),
+	action = purple_action_menu_new(_("Autoaccept File Transfers..."),
 					PURPLE_CALLBACK(set_auto_accept_settings), plugin, NULL);
 	(*menu) = g_list_prepend(*menu, action);
 }
--- a/libpurple/plugins/buddynote.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/plugins/buddynote.c	Fri Mar 22 01:54:47 2019 +0000
@@ -18,11 +18,11 @@
  */
 #include "internal.h"
 
+#include <action.h>
 #include <debug.h>
 #include <notify.h>
 #include <request.h>
 #include <signals.h>
-#include <util.h>
 #include <version.h>
 
 static void
@@ -55,13 +55,13 @@
 static void
 buddynote_extended_menu_cb(PurpleBlistNode *node, GList **m)
 {
-	PurpleMenuAction *bna = NULL;
+	PurpleActionMenu *bna = NULL;
 
 	if (purple_blist_node_is_transient(node))
 		return;
 
 	*m = g_list_append(*m, bna);
-	bna = purple_menu_action_new(_("Edit Notes..."), PURPLE_CALLBACK(buddynote_edit_cb), NULL, NULL);
+	bna = purple_action_menu_new(_("Edit Notes..."), PURPLE_CALLBACK(buddynote_edit_cb), NULL, NULL);
 	*m = g_list_append(*m, bna);
 }
 
--- a/libpurple/protocol.h	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocol.h	Fri Mar 22 01:54:47 2019 +0000
@@ -158,7 +158,7 @@
  * @status_text:     Gets a short string representing this buddy's status. This
  *                   will be shown on the buddy list.
  * @tooltip_text:    Allows the protocol to add text to a buddy's tooltip.
- * @blist_node_menu: Returns a list of #PurpleMenuAction structs, which
+ * @blist_node_menu: Returns a list of #PurpleActionMenu structs, which
  *                   represent extra actions to be shown in (for example) the
  *                   right-click menu for @node.
  * @normalize: Convert the username @who to its canonical form. Also checks for
--- a/libpurple/protocols.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols.c	Fri Mar 22 01:54:47 2019 +0000
@@ -538,61 +538,6 @@
 	return purple_protocol_client_iface_get_max_message_size(protocol, NULL);
 }
 
-/**************************************************************************/
-/* Protocol Action API                                                    */
-/**************************************************************************/
-
-PurpleProtocolAction *
-purple_protocol_action_new(const char* label,
-		PurpleProtocolActionCallback callback)
-{
-	PurpleProtocolAction *action;
-
-	g_return_val_if_fail(label != NULL && callback != NULL, NULL);
-
-	action = g_new0(PurpleProtocolAction, 1);
-
-	action->label    = g_strdup(label);
-	action->callback = callback;
-
-	return action;
-}
-
-void
-purple_protocol_action_free(PurpleProtocolAction *action)
-{
-	g_return_if_fail(action != NULL);
-
-	g_free(action->label);
-	g_free(action);
-}
-
-/**************************************************************************
- * GBoxed code for PurpleProtocolAction
- **************************************************************************/
-
-static PurpleProtocolAction *
-purple_protocol_action_copy(PurpleProtocolAction *action)
-{
-	g_return_val_if_fail(action != NULL, NULL);
-
-	return purple_protocol_action_new(action->label, action->callback);
-}
-
-GType
-purple_protocol_action_get_type(void)
-{
-	static GType type = 0;
-
-	if (type == 0) {
-		type = g_boxed_type_register_static("PurpleProtocolAction",
-				(GBoxedCopyFunc)purple_protocol_action_copy,
-				(GBoxedFreeFunc)purple_protocol_action_free);
-	}
-
-	return type;
-}
-
 /**************************************************************************
  * Protocols API
  **************************************************************************/
--- a/libpurple/protocols.h	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols.h	Fri Mar 22 01:54:47 2019 +0000
@@ -31,11 +31,6 @@
 
 #define PURPLE_PROTOCOLS_DOMAIN  (g_quark_from_static_string("protocols"))
 
-#define PURPLE_TYPE_PROTOCOL_ACTION  (purple_protocol_action_get_type())
-
-typedef struct _PurpleProtocolAction PurpleProtocolAction;
-typedef void (*PurpleProtocolActionCallback)(PurpleProtocolAction *action);
-
 /**************************************************************************/
 /* Basic Protocol Information                                             */
 /**************************************************************************/
@@ -128,19 +123,6 @@
 	gboolean secret;
 };
 
-/**
- * PurpleProtocolAction:
- *
- * Represents an action that the protocol can perform. This shows up in the
- * Accounts menu, under a submenu with the name of the account.
- */
-struct _PurpleProtocolAction {
-	char *label;
-	PurpleProtocolActionCallback callback;
-	PurpleConnection *connection;
-	gpointer user_data;
-};
-
 G_BEGIN_DECLS
 
 /**************************************************************************/
@@ -148,37 +130,6 @@
 /**************************************************************************/
 
 /**************************************************************************/
-/* Protocol Action API                                                    */
-/**************************************************************************/
-
-/**
- * purple_protocol_action_get_type:
- *
- * Returns: The #GType for the #PurpleProtocolAction boxed structure.
- */
-GType purple_protocol_action_get_type(void);
-
-/**
- * purple_protocol_action_new:
- * @label:    The description of the action to show to the user.
- * @callback: (scope call): The callback to call when the user selects this
- *            action.
- *
- * Allocates and returns a new PurpleProtocolAction. Use this to add actions in
- * a list in the get_actions function of the protocol.
- */
-PurpleProtocolAction *purple_protocol_action_new(const char* label,
-		PurpleProtocolActionCallback callback);
-
-/**
- * purple_protocol_action_free:
- * @action: The PurpleProtocolAction to free.
- *
- * Frees a PurpleProtocolAction
- */
-void purple_protocol_action_free(PurpleProtocolAction *action);
-
-/**************************************************************************/
 /* Protocol Chat Entry API                                                */
 /**************************************************************************/
 
--- a/libpurple/protocols/facebook/facebook.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/facebook/facebook.c	Fri Mar 22 01:54:47 2019 +0000
@@ -23,6 +23,7 @@
 
 #include "account.h"
 #include "accountopt.h"
+#include "action.h"
 #include "blistnode.h"
 #include "buddy.h"
 #include "buddyicon.h"
@@ -1158,7 +1159,7 @@
 	GList *acts = NULL;
 	PurpleAccount *acct;
 	PurpleConnection *gc;
-	PurpleMenuAction *act;
+	PurpleActionMenu *act;
 
 	if (!PURPLE_IS_BUDDY(node)) {
 		return NULL;
@@ -1168,7 +1169,7 @@
 	gc = purple_account_get_connection(acct);
 	fata = purple_connection_get_protocol_data(gc);
 
-	act = purple_menu_action_new(_("Initiate _Chat"),
+	act = purple_action_menu_new(_("Initiate _Chat"),
 	                             PURPLE_CALLBACK(fb_blist_chat_init),
 	                             fata, NULL);
 	acts = g_list_prepend(acts, act);
--- a/libpurple/protocols/gg/gg.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/gg/gg.c	Fri Mar 22 01:54:47 2019 +0000
@@ -28,6 +28,7 @@
 
 #include <internal.h>
 
+#include "action.h"
 #include "plugins.h"
 #include "version.h"
 #include "notify.h"
--- a/libpurple/protocols/irc/irc.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/irc/irc.c	Fri Mar 22 01:54:47 2019 +0000
@@ -26,6 +26,7 @@
 #include "internal.h"
 
 #include "accountopt.h"
+#include "action.h"
 #include "buddylist.h"
 #include "conversation.h"
 #include "core.h"
--- a/libpurple/protocols/jabber/buddy.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/jabber/buddy.c	Fri Mar 22 01:54:47 2019 +0000
@@ -29,6 +29,7 @@
 #include "util.h"
 #include "xmlnode.h"
 
+#include "action.h"
 #include "buddy.h"
 #include "chat.h"
 #include "jabber.h"
@@ -1851,7 +1852,7 @@
 	GList *jbrs;
 
 	GList *m = NULL;
-	PurpleMenuAction *act;
+	PurpleActionMenu *act;
 
 	if(!jb)
 		return m;
@@ -1859,11 +1860,11 @@
 	if (js->protocol_version.major == 0 && js->protocol_version.minor == 9 &&
 			jb != js->user_jb) {
 		if(jb->invisible & JABBER_INVIS_BUDDY) {
-			act = purple_menu_action_new(_("Un-hide From"),
+			act = purple_action_menu_new(_("Un-hide From"),
 			                           PURPLE_CALLBACK(jabber_buddy_make_visible),
 			                           NULL, NULL);
 		} else {
-			act = purple_menu_action_new(_("Temporarily Hide From"),
+			act = purple_action_menu_new(_("Temporarily Hide From"),
 			                           PURPLE_CALLBACK(jabber_buddy_make_invisible),
 			                           NULL, NULL);
 		}
@@ -1871,14 +1872,14 @@
 	}
 
 	if(jb->subscription & JABBER_SUB_FROM && jb != js->user_jb) {
-		act = purple_menu_action_new(_("Cancel Presence Notification"),
+		act = purple_action_menu_new(_("Cancel Presence Notification"),
 		                           PURPLE_CALLBACK(jabber_buddy_cancel_presence_notification),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
 	if(!(jb->subscription & JABBER_SUB_TO)) {
-		act = purple_menu_action_new(_("(Re-)Request authorization"),
+		act = purple_action_menu_new(_("(Re-)Request authorization"),
 		                           PURPLE_CALLBACK(jabber_buddy_rerequest_auth),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
@@ -1887,14 +1888,14 @@
 
 		/* shouldn't this just happen automatically when the buddy is
 		   removed? */
-		act = purple_menu_action_new(_("Unsubscribe"),
+		act = purple_action_menu_new(_("Unsubscribe"),
 		                           PURPLE_CALLBACK(jabber_buddy_unsubscribe),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
 	if (js->googletalk) {
-		act = purple_menu_action_new(_("Initiate _Chat"),
+		act = purple_action_menu_new(_("Initiate _Chat"),
 		                           PURPLE_CALLBACK(google_buddy_node_chat),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
@@ -1911,11 +1912,11 @@
 	 * TODO: Use disco#info...
 	 */
 	if (strchr(name, '@') == NULL) {
-		act = purple_menu_action_new(_("Log In"),
+		act = purple_action_menu_new(_("Log In"),
 									 PURPLE_CALLBACK(jabber_buddy_login),
 									 NULL, NULL);
 		m = g_list_append(m, act);
-		act = purple_menu_action_new(_("Log Out"),
+		act = purple_action_menu_new(_("Log Out"),
 									 PURPLE_CALLBACK(jabber_buddy_logout),
 									 NULL, NULL);
 		m = g_list_append(m, act);
@@ -1929,7 +1930,7 @@
 			continue;
 		for(commands = jbr->commands; commands; commands = g_list_next(commands)) {
 			JabberAdHocCommands *cmd = commands->data;
-			act = purple_menu_action_new(cmd->name, PURPLE_CALLBACK(jabber_adhoc_execute_action), cmd, NULL);
+			act = purple_action_menu_new(cmd->name, PURPLE_CALLBACK(jabber_adhoc_execute_action), cmd, NULL);
 			m = g_list_append(m, act);
 		}
 	}
--- a/libpurple/protocols/jabber/buddy.h	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/jabber/buddy.h	Fri Mar 22 01:54:47 2019 +0000
@@ -26,6 +26,8 @@
 
 typedef struct _JabberBuddy JabberBuddy;
 
+#include "action.h"
+
 #include "jabber.h"
 #include "caps.h"
 #include "jutil.h"
--- a/libpurple/protocols/novell/novell.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/novell/novell.c	Fri Mar 22 01:54:47 2019 +0000
@@ -21,6 +21,7 @@
 #include "internal.h"
 
 #include "accountopt.h"
+#include "action.h"
 #include "debug.h"
 #include "plugins.h"
 #include "server.h"
@@ -3448,10 +3449,10 @@
 novell_blist_node_menu(PurpleBlistNode *node)
 {
 	GList *list = NULL;
-	PurpleMenuAction *act;
+	PurpleActionMenu *act;
 
 	if(PURPLE_IS_BUDDY(node)) {
-		act = purple_menu_action_new(_("Initiate _Chat"),
+		act = purple_action_menu_new(_("Initiate _Chat"),
 		                           PURPLE_CALLBACK(_initiate_conference_cb),
 		                           NULL, NULL);
 		list = g_list_append(list, act);
--- a/libpurple/protocols/null/nullprpl.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/null/nullprpl.c	Fri Mar 22 01:54:47 2019 +0000
@@ -327,7 +327,7 @@
   purple_debug_info("nullprpl", "providing buddy list context menu item\n");
 
   if (PURPLE_IS_BUDDY(node)) {
-    PurpleMenuAction *action = purple_menu_action_new(
+    PurpleActionMenu *action = purple_action_menu_new(
       _("NullProtocol example menu item"),
       PURPLE_CALLBACK(blist_example_menu_item),
       NULL,   /* userdata passed to the callback */
--- a/libpurple/protocols/oscar/oscar.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/oscar/oscar.c	Fri Mar 22 01:54:47 2019 +0000
@@ -4918,7 +4918,7 @@
 	PurpleConnection *gc;
 	OscarData *od;
 	GList *menu;
-	PurpleMenuAction *act;
+	PurpleActionMenu *act;
 	aim_userinfo_t *userinfo;
 	PurpleAccount *account;
 	const char *bname = purple_buddy_get_name(buddy);
@@ -4931,7 +4931,7 @@
 
 	if (od->icq && oscar_util_valid_name_icq(bname))
 	{
-		act = purple_menu_action_new(_("Get AIM Info"),
+		act = purple_action_menu_new(_("Get AIM Info"),
 								   PURPLE_CALLBACK(oscar_get_aim_info_cb),
 								   NULL, NULL);
 		menu = g_list_prepend(menu, act);
@@ -4940,7 +4940,7 @@
 	if (purple_buddy_get_group(buddy) != NULL)
 	{
 		/* We only do this if the user is in our buddy list */
-		act = purple_menu_action_new(_("Edit Buddy Comment"),
+		act = purple_action_menu_new(_("Edit Buddy Comment"),
 		                           PURPLE_CALLBACK(oscar_buddycb_edit_comment),
 		                           NULL, NULL);
 		menu = g_list_prepend(menu, act);
@@ -4948,7 +4948,7 @@
 
 	if (od->icq)
 	{
-		act = purple_menu_action_new(_("Get X-Status Msg"),
+		act = purple_action_menu_new(_("Get X-Status Msg"),
 		                           PURPLE_CALLBACK(oscar_get_icqxstatusmsg),
 		                           NULL, NULL);
 		menu = g_list_prepend(menu, act);
@@ -4966,13 +4966,13 @@
 		{
 			if (conn)
 			{
-				act = purple_menu_action_new(_("End Direct IM Session"),
+				act = purple_action_menu_new(_("End Direct IM Session"),
 				                          PURPLE_CALLBACK(oscar_close_directim),
 				                          NULL, NULL);
 			}
 			else
 			{
-				act = purple_menu_action_new(_("Direct IM"),
+				act = purple_action_menu_new(_("Direct IM"),
 				                          PURPLE_CALLBACK(oscar_ask_directim),
 				                          NULL, NULL);
 			}
@@ -4990,7 +4990,7 @@
 		gname = aim_ssi_itemlist_findparentname(&od->ssi.local, bname);
 		if (gname && aim_ssi_waitingforauth(&od->ssi.local, gname, bname))
 		{
-			act = purple_menu_action_new(_("Re-request Authorization"),
+			act = purple_action_menu_new(_("Re-request Authorization"),
 			                           PURPLE_CALLBACK(oscar_auth_sendrequest_menu),
 			                           NULL, NULL);
 			menu = g_list_prepend(menu, act);
--- a/libpurple/protocols/oscar/visibility.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/oscar/visibility.c	Fri Mar 22 01:54:47 2019 +0000
@@ -69,7 +69,7 @@
 	}
 }
 
-PurpleMenuAction *
+PurpleActionMenu *
 create_visibility_menu_item(OscarData *od, const char *bname)
 {
 	PurpleAccount *account = purple_connection_get_account(od->gc);
@@ -82,7 +82,7 @@
 	} else {
 		label = on_list ? _(DONT_APPEAR_OFFLINE) : _(APPEAR_OFFLINE);
 	}
-	return purple_menu_action_new(label, PURPLE_CALLBACK(visibility_cb), NULL, NULL);
+	return purple_action_menu_new(label, PURPLE_CALLBACK(visibility_cb), NULL, NULL);
 }
 
 static void
--- a/libpurple/protocols/oscar/visibility.h	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/oscar/visibility.h	Fri Mar 22 01:54:47 2019 +0000
@@ -23,9 +23,9 @@
 
 #include "oscar.h"
 #include "plugins.h"
-#include "util.h"
+#include "action.h"
 
-PurpleMenuAction * create_visibility_menu_item(OscarData *od, const char *bname);
+PurpleActionMenu * create_visibility_menu_item(OscarData *od, const char *bname);
 void oscar_show_visible_list(PurpleProtocolAction *action);
 void oscar_show_invisible_list(PurpleProtocolAction *action);
 
--- a/libpurple/protocols/sametime/sametime.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/sametime/sametime.c	Fri Mar 22 01:54:47 2019 +0000
@@ -33,6 +33,7 @@
 /* purple includes */
 #include "account.h"
 #include "accountopt.h"
+#include "action.h"
 #include "circularbuffer.h"
 #include "conversation.h"
 #include "debug.h"
@@ -43,7 +44,6 @@
 #include "plugins.h"
 #include "protocol.h"
 #include "request.h"
-#include "util.h"
 #include "version.h"
 
 /* meanwhile includes */
@@ -1337,7 +1337,7 @@
                                GList **menu, struct mwPurpleProtocolData *pd) {
   const char *owner;
   PurpleAccount *acct;
-  PurpleMenuAction *act;
+  PurpleActionMenu *act;
 
   /* we only want groups */
   if(! PURPLE_IS_GROUP(node)) return;
@@ -1351,7 +1351,7 @@
   /* check if it's a NAB group for this account */
   owner = purple_blist_node_get_string(node, GROUP_KEY_OWNER);
   if(owner && purple_strequal(owner, purple_account_get_username(acct))) {
-    act = purple_menu_action_new(_("Get Notes Address Book Info"),
+    act = purple_action_menu_new(_("Get Notes Address Book Info"),
                                PURPLE_CALLBACK(blist_menu_nab), pd, NULL);
     *menu = g_list_append(*menu, act);
   }
@@ -3535,14 +3535,14 @@
 
 static GList *mw_protocol_blist_node_menu(PurpleBlistNode *node) {
   GList *l = NULL;
-  PurpleMenuAction *act;
+  PurpleActionMenu *act;
 
   if(! PURPLE_IS_BUDDY(node))
     return l;
 
   l = g_list_append(l, NULL);
 
-  act = purple_menu_action_new(_("Invite to Conference..."),
+  act = purple_action_menu_new(_("Invite to Conference..."),
                              PURPLE_CALLBACK(blist_menu_conf), NULL, NULL);
   l = g_list_append(l, act);
 
--- a/libpurple/protocols/silc/buddy.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/silc/buddy.c	Fri Mar 22 01:54:47 2019 +0000
@@ -1663,7 +1663,7 @@
 	SilcClientConnection conn = sg->conn;
 	const char *pkfile = NULL;
 	SilcClientEntry client_entry = NULL;
-	PurpleMenuAction *act;
+	PurpleActionMenu *act;
 	GList *m = NULL;
 	SilcPurpleBuddyWb wb;
 
@@ -1675,37 +1675,37 @@
 	if (client_entry &&
 	    silc_client_private_message_key_is_set(sg->client,
 						   sg->conn, client_entry)) {
-		act = purple_menu_action_new(_("Reset IM Key"),
+		act = purple_action_menu_new(_("Reset IM Key"),
 		                           PURPLE_CALLBACK(silcpurple_buddy_resetkey),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
 	} else {
-		act = purple_menu_action_new(_("IM with Key Exchange"),
+		act = purple_action_menu_new(_("IM with Key Exchange"),
 		                           PURPLE_CALLBACK(silcpurple_buddy_keyagr),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
 
-		act = purple_menu_action_new(_("IM with Password"),
+		act = purple_action_menu_new(_("IM with Password"),
 		                           PURPLE_CALLBACK(silcpurple_buddy_privkey_menu),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
 	if (pkfile) {
-		act = purple_menu_action_new(_("Show Public Key"),
+		act = purple_action_menu_new(_("Show Public Key"),
 		                           PURPLE_CALLBACK(silcpurple_buddy_showkey),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
 
 	} else {
-		act = purple_menu_action_new(_("Get Public Key..."),
+		act = purple_action_menu_new(_("Get Public Key..."),
 		                           PURPLE_CALLBACK(silcpurple_buddy_getkey_menu),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
 	if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) {
-		act = purple_menu_action_new(_("Kill User"),
+		act = purple_action_menu_new(_("Kill User"),
 		                           PURPLE_CALLBACK(silcpurple_buddy_kill),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
@@ -1715,7 +1715,7 @@
 		wb = silc_calloc(1, sizeof(*wb));
 		wb->sg = sg;
 		wb->client_entry = client_entry;
-		act = purple_menu_action_new(_("Draw On Whiteboard"),
+		act = purple_action_menu_new(_("Draw On Whiteboard"),
 		                           PURPLE_CALLBACK(silcpurple_buddy_wb),
 		                           (void *)wb, NULL);
 		m = g_list_append(m, act);
--- a/libpurple/protocols/silc/chat.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/silc/chat.c	Fri Mar 22 01:54:47 2019 +0000
@@ -872,7 +872,7 @@
 	SilcUInt32 mode = 0;
 
 	GList *m = NULL;
-	PurpleMenuAction *act;
+	PurpleActionMenu *act;
 
 	if (components)
 		chname = g_hash_table_lookup(components, "channel");
@@ -889,31 +889,31 @@
 	if (strstr(chname, "[Private Group]"))
 		return NULL;
 
-	act = purple_menu_action_new(_("Get Info"),
+	act = purple_action_menu_new(_("Get Info"),
 	                           PURPLE_CALLBACK(silcpurple_chat_getinfo_menu),
 	                           NULL, NULL);
 	m = g_list_append(m, act);
 
 	if (chu) {
-		act = purple_menu_action_new(_("Add Private Group"),
+		act = purple_action_menu_new(_("Add Private Group"),
 		                           PURPLE_CALLBACK(silcpurple_chat_prv),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
 	}
 
 	if (chu && mode & SILC_CHANNEL_UMODE_CHANFO) {
-		act = purple_menu_action_new(_("Channel Authentication"),
+		act = purple_action_menu_new(_("Channel Authentication"),
 		                           PURPLE_CALLBACK(silcpurple_chat_chauth),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
 
 		if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
-			act = purple_menu_action_new(_("Reset Permanent"),
+			act = purple_action_menu_new(_("Reset Permanent"),
 			                           PURPLE_CALLBACK(silcpurple_chat_permanent_reset),
 			                           NULL, NULL);
 			m = g_list_append(m, act);
 		} else {
-			act = purple_menu_action_new(_("Set Permanent"),
+			act = purple_action_menu_new(_("Set Permanent"),
 			                           PURPLE_CALLBACK(silcpurple_chat_permanent),
 			                           NULL, NULL);
 			m = g_list_append(m, act);
@@ -921,42 +921,42 @@
 	}
 
 	if (chu && mode & SILC_CHANNEL_UMODE_CHANOP) {
-		act = purple_menu_action_new(_("Set User Limit"),
+		act = purple_action_menu_new(_("Set User Limit"),
 		                           PURPLE_CALLBACK(silcpurple_chat_ulimit),
 		                           NULL, NULL);
 		m = g_list_append(m, act);
 
 		if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
-			act = purple_menu_action_new(_("Reset Topic Restriction"),
+			act = purple_action_menu_new(_("Reset Topic Restriction"),
 			                           PURPLE_CALLBACK(silcpurple_chat_resettopic),
 			                           NULL, NULL);
 			m = g_list_append(m, act);
 		} else {
-			act = purple_menu_action_new(_("Set Topic Restriction"),
+			act = purple_action_menu_new(_("Set Topic Restriction"),
 			                           PURPLE_CALLBACK(silcpurple_chat_settopic),
 			                           NULL, NULL);
 			m = g_list_append(m, act);
 		}
 
 		if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) {
-			act = purple_menu_action_new(_("Reset Private Channel"),
+			act = purple_action_menu_new(_("Reset Private Channel"),
 			                           PURPLE_CALLBACK(silcpurple_chat_resetprivate),
 			                           NULL, NULL);
 			m = g_list_append(m, act);
 		} else {
-			act = purple_menu_action_new(_("Set Private Channel"),
+			act = purple_action_menu_new(_("Set Private Channel"),
 			                           PURPLE_CALLBACK(silcpurple_chat_setprivate),
 			                           NULL, NULL);
 			m = g_list_append(m, act);
 		}
 
 		if (channel->mode & SILC_CHANNEL_MODE_SECRET) {
-			act = purple_menu_action_new(_("Reset Secret Channel"),
+			act = purple_action_menu_new(_("Reset Secret Channel"),
 			                           PURPLE_CALLBACK(silcpurple_chat_resetsecret),
 			                           NULL, NULL);
 			m = g_list_append(m, act);
 		} else {
-			act = purple_menu_action_new(_("Set Secret Channel"),
+			act = purple_action_menu_new(_("Set Secret Channel"),
 			                           PURPLE_CALLBACK(silcpurple_chat_setsecret),
 			                           NULL, NULL);
 			m = g_list_append(m, act);
@@ -968,7 +968,7 @@
 		wb = silc_calloc(1, sizeof(*wb));
 		wb->sg = sg;
 		wb->channel = channel;
-		act = purple_menu_action_new(_("Draw On Whiteboard"),
+		act = purple_action_menu_new(_("Draw On Whiteboard"),
 		                           PURPLE_CALLBACK(silcpurple_chat_wb),
 		                           (void *)wb, NULL);
 		m = g_list_append(m, act);
--- a/libpurple/protocols/zephyr/zephyr.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/protocols/zephyr/zephyr.c	Fri Mar 22 01:54:47 2019 +0000
@@ -29,6 +29,7 @@
 #include "libpurple/internal.h"
 
 #include "accountopt.h"
+#include "action.h"
 #include "debug.h"
 #include "notify.h"
 #include "plugins.h"
--- a/libpurple/tests/meson.build	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/tests/meson.build	Fri Mar 22 01:54:47 2019 +0000
@@ -2,6 +2,7 @@
     'attention_type',
     'image',
     'protocol_attention',
+    'protocol_action',
     'protocol_xfer',
     'queued_output_stream',
     'smiley',
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/tests/test_protocol_action.c	Fri Mar 22 01:54:47 2019 +0000
@@ -0,0 +1,96 @@
+/*
+ * Purple
+ *
+ * Purple is the legal property of its developers, whose names are too
+ * numerous to list here. Please refer to the COPYRIGHT file distributed
+ * with this source distribution
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+#include <glib.h>
+
+#include <purple.h>
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+_test_purple_protocol_action_callback(PurpleProtocolAction *action) {
+}
+
+/******************************************************************************
+ * Tests
+ *****************************************************************************/
+static void
+test_purple_protocol_action_new(void) {
+	PurpleProtocolAction *action = NULL;
+
+	action = purple_protocol_action_new(
+		"label",
+		_test_purple_protocol_action_callback
+	);
+	g_assert_nonnull(action);
+
+	g_assert_cmpstr(action->label, ==, "label");
+	g_assert(action->callback == _test_purple_protocol_action_callback);
+
+	purple_protocol_action_free(action);
+}
+
+static void
+test_purple_protocol_action_copy(void) {
+	PurpleProtocolAction *orig = NULL, *copy = NULL;
+
+	orig = purple_protocol_action_new(
+		"label",
+		_test_purple_protocol_action_callback
+	);
+	g_assert_nonnull(orig);
+
+	copy = purple_protocol_action_copy(orig);
+	purple_protocol_action_free(orig);
+
+	g_assert_cmpstr(copy->label, ==, "label");
+	g_assert(copy->callback == _test_purple_protocol_action_callback);
+
+	purple_protocol_action_free(copy);
+}
+
+/******************************************************************************
+ * Main
+ *****************************************************************************/
+gint
+main(gint argc, gchar **argv) {
+	gint res = 0;
+
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_set_nonfatal_assertions();
+
+	g_test_add_func(
+		"/protocol-action/new",
+		test_purple_protocol_action_new
+	);
+
+	g_test_add_func(
+		"/protocol-action/copy",
+		test_purple_protocol_action_copy
+	);
+
+	res = g_test_run();
+
+	return res;
+}
--- a/libpurple/util.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/util.c	Fri Mar 22 01:54:47 2019 +0000
@@ -29,15 +29,6 @@
 
 #include <json-glib/json-glib.h>
 
-struct _PurpleMenuAction
-{
-	char *label;
-	PurpleCallback callback;
-	gpointer data;
-	GList *children;
-	gchar *stock_icon;
-};
-
 static char *custom_user_dir = NULL;
 static char *user_dir = NULL;
 static gchar *cache_dir = NULL;
@@ -47,99 +38,6 @@
 static JsonNode *escape_js_node = NULL;
 static JsonGenerator *escape_js_gen = NULL;
 
-PurpleMenuAction *
-purple_menu_action_new(const char *label, PurpleCallback callback, gpointer data,
-                     GList *children)
-{
-	PurpleMenuAction *act = g_new0(PurpleMenuAction, 1);
-	act->label = g_strdup(label);
-	act->callback = callback;
-	act->data = data;
-	act->children = children;
-	return act;
-}
-
-void
-purple_menu_action_free(PurpleMenuAction *act)
-{
-	g_return_if_fail(act != NULL);
-
-	g_free(act->stock_icon);
-	g_free(act->label);
-	g_free(act);
-}
-
-char * purple_menu_action_get_label(const PurpleMenuAction *act)
-{
-	g_return_val_if_fail(act != NULL, NULL);
-
-	return act->label;
-}
-
-PurpleCallback purple_menu_action_get_callback(const PurpleMenuAction *act)
-{
-	g_return_val_if_fail(act != NULL, NULL);
-
-	return act->callback;
-}
-
-gpointer purple_menu_action_get_data(const PurpleMenuAction *act)
-{
-	g_return_val_if_fail(act != NULL, NULL);
-
-	return act->data;
-}
-
-GList* purple_menu_action_get_children(const PurpleMenuAction *act)
-{
-	g_return_val_if_fail(act != NULL, NULL);
-
-	return act->children;
-}
-
-void purple_menu_action_set_label(PurpleMenuAction *act, char *label)
-{
-	g_return_if_fail(act != NULL);
-
-	act-> label = label;
-}
-
-void purple_menu_action_set_callback(PurpleMenuAction *act, PurpleCallback callback)
-{
-	g_return_if_fail(act != NULL);
-
-	act->callback = callback;
-}
-
-void purple_menu_action_set_data(PurpleMenuAction *act, gpointer data)
-{
-	g_return_if_fail(act != NULL);
-
-	act->data = data;
-}
-
-void purple_menu_action_set_children(PurpleMenuAction *act, GList *children)
-{
-	g_return_if_fail(act != NULL);
-
-	act->children = children;
-}
-
-void purple_menu_action_set_stock_icon(PurpleMenuAction *act,
-	const gchar *stock)
-{
-	g_return_if_fail(act != NULL);
-
-	g_free(act->stock_icon);
-	act->stock_icon = g_strdup(stock);
-}
-
-const gchar *
-purple_menu_action_get_stock_icon(PurpleMenuAction *act)
-{
-	return act->stock_icon;
-}
-
 void
 purple_util_init(void)
 {
--- a/libpurple/util.h	Wed Mar 20 19:06:31 2019 -0400
+++ b/libpurple/util.h	Fri Mar 22 01:54:47 2019 +0000
@@ -34,15 +34,6 @@
 #include <stdio.h>
 
 /**
- * PurpleMenuAction:
- *
- * A generic structure that contains information about an "action."  One
- * place this is is used is by protocols to tell the core the list of available
- * right-click actions for a buddy list row.
- */
-typedef struct _PurpleMenuAction PurpleMenuAction;
-
-/**
  * PurpleKeyValuePair:
  * @key: The key
  * @value: The value
@@ -73,127 +64,6 @@
 G_BEGIN_DECLS
 
 /**
- * purple_menu_action_new:
- * @label:    The text label to display for this action.
- * @callback: The function to be called when the action is used on
- *                 the selected item.
- * @data:     Additional data to be passed to the callback.
- * @children: (element-type PurpleMenuAction) (transfer full): Menu actions to
- *            be added as a submenu of this action.
- *
- * Creates a new PurpleMenuAction.
- *
- * Returns: The PurpleMenuAction.
- */
-PurpleMenuAction *purple_menu_action_new(const char *label, PurpleCallback callback,
-                                     gpointer data, GList *children);
-
-/**
- * purple_menu_action_free:
- * @act: The PurpleMenuAction to free.
- *
- * Frees a PurpleMenuAction
- */
-void purple_menu_action_free(PurpleMenuAction *act);
-
-/**
- * purple_menu_action_get_label:
- * @act:	The PurpleMenuAction.
- *
- * Returns the label of the PurpleMenuAction.
- *
- * Returns: The label string.
- */
-char * purple_menu_action_get_label(const PurpleMenuAction *act);
-
-/**
- * purple_menu_action_get_callback:
- * @act:	The PurpleMenuAction.
- *
- * Returns the callback of the PurpleMenuAction.
- *
- * Returns: The callback function.
- */
-PurpleCallback purple_menu_action_get_callback(const PurpleMenuAction *act);
-
-/**
- * purple_menu_action_get_data:
- * @act:	The PurpleMenuAction.
- *
- * Returns the data stored in the PurpleMenuAction.
- *
- * Returns: The data.
- */
-gpointer purple_menu_action_get_data(const PurpleMenuAction *act);
-
-/**
- * purple_menu_action_get_children:
- * @act:	The PurpleMenuAction.
- *
- * Returns the children of the PurpleMenuAction.
- *
- * Returns: (element-type PurpleMenuAction) (transfer none): The menu children.
- */
-GList* purple_menu_action_get_children(const PurpleMenuAction *act);
-
-/**
- * purple_menu_action_set_label:
- * @act:   The menu action.
- * @label: The label for the menu action.
- *
- * Set the label to the PurpleMenuAction.
- */
-void purple_menu_action_set_label(PurpleMenuAction *act, char *label);
-
-/**
- * purple_menu_action_set_callback:
- * @act:        The menu action.
- * @callback:   The callback.
- *
- * Set the callback that will be used by the PurpleMenuAction.
- */
-void purple_menu_action_set_callback(PurpleMenuAction *act, PurpleCallback callback);
-
-/**
- * purple_menu_action_set_data:
- * @act:   The menu action.
- * @data:  The data used by this PurpleMenuAction
- *
- * Set the label to the PurpleMenuAction.
- */
-void purple_menu_action_set_data(PurpleMenuAction *act, gpointer data);
-
-/**
- * purple_menu_action_set_children:
- * @act:       The menu action.
- * @children: (element-type PurpleMenuAction) (transfer full): The menu children
- *
- * Set the children of the PurpleMenuAction.
- */
-void purple_menu_action_set_children(PurpleMenuAction *act, GList *children);
-
-/**
- * purple_menu_action_set_stock_icon:
- * @act:   The menu action.
- * @stock: The stock icon identifier.
- *
- * Sets the icon for the PurpleMenuAction.
- */
-void purple_menu_action_set_stock_icon(PurpleMenuAction *act,
-	const gchar *stock);
-
-/**
- * purple_menu_action_get_stock_icon:
- * @act: The menu action.
- *
- * Gets the stock icon of the PurpleMenuAction.
- *
- * Returns: The stock icon identifier.
- */
-const gchar *
-purple_menu_action_get_stock_icon(PurpleMenuAction *act);
-
-/**
  * purple_util_set_current_song:
  * @title:     The title of the song, %NULL to unset the value.
  * @artist:    The artist of the song, can be %NULL.
--- a/pidgin/gtkblist.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/pidgin/gtkblist.c	Fri Mar 22 01:54:47 2019 +0000
@@ -23,6 +23,7 @@
 #include "pidgin.h"
 
 #include "account.h"
+#include "action.h"
 #include "connection.h"
 #include "core.h"
 #include "debug.h"
@@ -1411,7 +1412,7 @@
 		return;
 
 	for(l = ll = purple_protocol_client_iface_blist_node_menu(protocol, node); l; l = l->next) {
-		PurpleMenuAction *act = (PurpleMenuAction *) l->data;
+		PurpleActionMenu *act = (PurpleActionMenu *) l->data;
 		pidgin_append_menu_action(menu, act, node);
 	}
 	g_list_free(ll);
@@ -1423,7 +1424,7 @@
 	GList *l, *ll;
 
 	for(l = ll = purple_blist_node_get_extended_menu(node); l; l = l->next) {
-		PurpleMenuAction *act = (PurpleMenuAction *) l->data;
+		PurpleActionMenu *act = (PurpleActionMenu *) l->data;
 		pidgin_append_menu_action(menu, act, node);
 	}
 	g_list_free(ll);
--- a/pidgin/gtkconv.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/pidgin/gtkconv.c	Fri Mar 22 01:54:47 2019 +0000
@@ -32,6 +32,7 @@
 
 #include "account.h"
 #include "attention.h"
+#include "action.h"
 #include "cmds.h"
 #include "core.h"
 #include "debug.h"
@@ -3554,7 +3555,7 @@
 	}
 
 	for(; list; list = g_list_delete_link(list, list)) {
-		PurpleMenuAction *act = (PurpleMenuAction *) list->data;
+		PurpleActionMenu *act = (PurpleActionMenu *) list->data;
 		item = pidgin_append_menu_action(menu, act, conv);
 		action_items = g_list_prepend(action_items, item);
 		gtk_widget_show_all(item);
@@ -4235,7 +4236,7 @@
 
 	menu_actions = purple_e2ee_provider_get_conv_menu_actions(provider, conv);
 	for (it = menu_actions; it; it = g_list_next(it)) {
-		PurpleMenuAction *action = it->data;
+		PurpleActionMenu *action = it->data;
 
 		gtk_widget_show_all(
 			pidgin_append_menu_action(menu, action, conv));
--- a/pidgin/gtkutils.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/pidgin/gtkutils.c	Fri Mar 22 01:54:47 2019 +0000
@@ -1754,7 +1754,7 @@
 }
 
 GtkWidget *
-pidgin_append_menu_action(GtkWidget *menu, PurpleMenuAction *act,
+pidgin_append_menu_action(GtkWidget *menu, PurpleActionMenu *act,
                             gpointer object)
 {
 	GtkWidget *menuitem;
@@ -1766,7 +1766,7 @@
 		return pidgin_separator(menu);
 	}
 
-	stock_id = purple_menu_action_get_stock_icon(act);
+	stock_id = purple_action_menu_get_stock_icon(act);
 	if (stock_id) {
 		icon_image = gtk_image_new_from_stock(stock_id,
 			gtk_icon_size_from_name(
@@ -1775,20 +1775,20 @@
 
 	if (icon_image) {
 		menuitem = gtk_image_menu_item_new_with_mnemonic(
-			purple_menu_action_get_label(act));
+			purple_action_menu_get_label(act));
 		gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem),
 			icon_image);
 	} else {
 		menuitem = gtk_menu_item_new_with_mnemonic(
-			purple_menu_action_get_label(act));
+			purple_action_menu_get_label(act));
 	}
 
-	list = purple_menu_action_get_children(act);
+	list = purple_action_menu_get_children(act);
 
 	if (list == NULL) {
 		PurpleCallback callback;
 
-		callback = purple_menu_action_get_callback(act);
+		callback = purple_action_menu_get_callback(act);
 
 		if (callback != NULL) {
 			g_object_set_data(G_OBJECT(menuitem),
@@ -1796,7 +1796,7 @@
 							  callback);
 			g_object_set_data(G_OBJECT(menuitem),
 							  "purplecallbackdata",
-							  purple_menu_action_get_data(act));
+							  purple_action_menu_get_data(act));
 			g_signal_connect(G_OBJECT(menuitem), "activate",
 							 G_CALLBACK(menu_action_cb),
 							 object);
@@ -1819,21 +1819,21 @@
 		if (group) {
 			char *path = g_strdup_printf("%s/%s",
 				gtk_menu_item_get_accel_path(GTK_MENU_ITEM(menuitem)),
-				purple_menu_action_get_label(act));
+				purple_action_menu_get_label(act));
 			gtk_menu_set_accel_path(GTK_MENU(submenu), path);
 			g_free(path);
 			gtk_menu_set_accel_group(GTK_MENU(submenu), group);
 		}
 
 		for (l = list; l; l = l->next) {
-			PurpleMenuAction *act = (PurpleMenuAction *)l->data;
+			PurpleActionMenu *act = (PurpleActionMenu *)l->data;
 
 			pidgin_append_menu_action(submenu, act, object);
 		}
 		g_list_free(list);
-		purple_menu_action_set_children(act, NULL);
+		purple_action_menu_set_children(act, NULL);
 	}
-	purple_menu_action_free(act);
+	purple_action_menu_free(act);
 	return menuitem;
 }
 
--- a/pidgin/gtkutils.h	Wed Mar 20 19:06:31 2019 -0400
+++ b/pidgin/gtkutils.h	Fri Mar 22 01:54:47 2019 +0000
@@ -30,6 +30,7 @@
 
 #include "gtkconv.h"
 #include "pidgin.h"
+#include "action.h"
 #include "protocol.h"
 #include "util.h"
 
@@ -554,14 +555,14 @@
 /**
  * pidgin_append_menu_action:
  * @menu:    The menu to append to.
- * @act:     The PurpleMenuAction to append.
+ * @act:     The PurpleActionMenu to append.
  * @gobject: The object to be passed to the action callback.
  *
- * Append a PurpleMenuAction to a menu.
+ * Append a PurpleActionMenu to a menu.
  *
  * Returns: (transfer full): The menuitem added.
  */
-GtkWidget *pidgin_append_menu_action(GtkWidget *menu, PurpleMenuAction *act,
+GtkWidget *pidgin_append_menu_action(GtkWidget *menu, PurpleActionMenu *act,
                                  gpointer gobject);
 
 /**
--- a/pidgin/plugins/gevolution/gevolution.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/pidgin/plugins/gevolution/gevolution.c	Fri Mar 22 01:54:47 2019 +0000
@@ -266,7 +266,7 @@
 static void
 blist_node_extended_menu_cb(PurpleBlistNode *node, GList **menu)
 {
-	PurpleMenuAction *act;
+	PurpleActionMenu *act;
 	PurpleBuddy *buddy;
 	PurpleAccount *account;
 	EContact *contact;
@@ -285,7 +285,7 @@
 
 	if (contact == NULL)
 	{
-		act = purple_menu_action_new(_("Add to Address Book"),
+		act = purple_action_menu_new(_("Add to Address Book"),
 		                           PURPLE_CALLBACK(menu_item_activate_cb),
 		                           NULL, NULL);
 		*menu = g_list_append(*menu, act);
@@ -297,7 +297,7 @@
 
 	if (mail != NULL)
 	{
-		act = purple_menu_action_new(_("Send Email"),
+		act = purple_action_menu_new(_("Send Email"),
 			PURPLE_CALLBACK(menu_item_send_mail_activate_cb), NULL, NULL);
 		*menu = g_list_append(*menu, act);
 		g_free(mail);
--- a/pidgin/plugins/markerline.c	Wed Mar 20 19:06:31 2019 -0400
+++ b/pidgin/plugins/markerline.c	Fri Mar 22 01:54:47 2019 +0000
@@ -36,6 +36,7 @@
 #include <gtkconv.h>
 #include <gtkplugin.h>
 #include <gtkwebview.h>
+#include <action.h>
 #include <version.h>
 
 #define PREF_PREFIX     "/plugins/gtk/" PLUGIN_ID
@@ -167,7 +168,7 @@
 {
 	gboolean enabled = ((PURPLE_IS_IM_CONVERSATION(conv) && purple_prefs_get_bool(PREF_IMS)) ||
 		(PURPLE_IS_CHAT_CONVERSATION(conv) && purple_prefs_get_bool(PREF_CHATS)));
-	PurpleMenuAction *action = purple_menu_action_new(_("Jump to markerline"),
+	PurpleActionMenu *action = purple_action_menu_new(_("Jump to markerline"),
 			enabled ? PURPLE_CALLBACK(jump_to_markerline) : NULL, NULL, NULL);
 	*list = g_list_append(*list, action);
 }

mercurial