diff --git a/doc/reference/libpurple/libpurple-docs.xml b/doc/reference/libpurple/libpurple-docs.xml
--- a/doc/reference/libpurple/libpurple-docs.xml
+++ b/doc/reference/libpurple/libpurple-docs.xml
@@ -80,6 +80,7 @@
       <xi:include href="xml/purplemarkup.xml" />
       <xi:include href="xml/purplenoopcredentialprovider.xml" />
       <xi:include href="xml/purpleoptions.xml" />
+      <xi:include href="xml/purpleplugininfo.xml" />
       <xi:include href="xml/purplepresence.xml" />
       <xi:include href="xml/purpleprotocolattention.xml" />
       <xi:include href="xml/purpleprotocolchat.xml" />
diff --git a/libpurple/meson.build b/libpurple/meson.build
--- a/libpurple/meson.build
+++ b/libpurple/meson.build
@@ -57,6 +57,7 @@
 	'purplemessage.c',
 	'purplenoopcredentialprovider.c',
 	'purpleoptions.c',
+	'purpleplugininfo.c',
 	'purplepresence.c',
 	'purpleprotocolattention.c',
 	'purpleprotocolchat.c',
@@ -149,6 +150,7 @@
 	'purplemessage.h',
 	'purplenoopcredentialprovider.h',
 	'purpleoptions.h',
+	'purpleplugininfo.h',
 	'purplepresence.h',
 	'purpleprotocolattention.h',
 	'purpleprotocolchat.h',
@@ -232,6 +234,7 @@
 	'purpleconversation.h',
 	'purpleimconversation.h',
 	'purplemessage.h',
+	'purpleplugininfo.h',
 	'roomlist.h',
 	'status.h',
 	'xfer.h',
diff --git a/libpurple/plugins.h b/libpurple/plugins.h
--- a/libpurple/plugins.h
+++ b/libpurple/plugins.h
@@ -57,13 +57,7 @@
 
 typedef GPluginPluginInterface PurplePluginInterface;
 
-/**
- * PURPLE_TYPE_PLUGIN_INFO:
- *
- * The standard _get_type macro for #PurplePluginInfo.
- */
-#define PURPLE_TYPE_PLUGIN_INFO (purple_plugin_info_get_type())
-typedef struct _PurplePluginInfo PurplePluginInfo;
+#include "purpleplugininfo.h"
 
 /**
  * PURPLE_TYPE_PLUGIN_ACTION:
@@ -73,8 +67,6 @@
 #define PURPLE_TYPE_PLUGIN_ACTION (purple_plugin_action_get_type())
 typedef struct _PurplePluginAction PurplePluginAction;
 
-#include "pluginpref.h"
-
 /**
  * PurplePluginActionCb:
  * @action: the action information.
@@ -84,69 +76,6 @@
 typedef void (*PurplePluginActionCb)(PurplePluginAction *action);
 
 /**
- * PurplePluginActionsCb:
- * @plugin: the plugin associated with this callback.
- *
- * Returns a list of actions the plugin can perform.
- *
- * Returns: (transfer none): A list of actions the plugin can perform.
- */
-typedef GList *(*PurplePluginActionsCb)(PurplePlugin *plugin);
-
-/**
- * PurplePluginExtraCb:
- * @plugin: the plugin associated with this callback.
- *
- * Gives extra information about the plguin.
- *
- * Returns: a newly allocated string denoting extra information
- * about a plugin.
- */
-typedef gchar *(*PurplePluginExtraCb)(PurplePlugin *plugin);
-
-/**
- * PurplePluginPrefFrameCb:
- * @plugin: the plugin associated with this callback.
- *
- * Returns the preferences frame for the plugin.
- *
- * Returns: Preference frame.
- */
-typedef PurplePluginPrefFrame *(*PurplePluginPrefFrameCb)(PurplePlugin *plugin);
-
-/**
- * PurplePrefRequestCb:
- *
- * Returns the preferences request handle for a plugin.
- *
- * Returns: Preferences request handle.
- */
-typedef gpointer (*PurplePluginPrefRequestCb)(PurplePlugin *plugin);
-
-/**
- * PurplePluginInfoFlags:
- * @PURPLE_PLUGIN_INFO_FLAGS_INTERNAL:  Plugin is not shown in UI lists
- * @PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD: Auto-load the plugin
- *
- * Flags that can be used to treat plugins differently.
- */
-typedef enum /*< flags >*/
-{
-	PURPLE_PLUGIN_INFO_FLAGS_INTERNAL  = 1 << 1,
-	PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD = 1 << 2,
-
-} PurplePluginInfoFlags;
-
-/**
- * PurplePluginInfo:
- *
- * Holds information about a plugin.
- */
-struct _PurplePluginInfo {
-	GPluginPluginInfo parent;
-};
-
-/**
  * PurplePluginAction:
  * @label: The label to display in the user interface.
  * @callback: The function to call when the user wants to perform this action.
@@ -164,41 +93,6 @@
 };
 
 /**
- * PURPLE_PLUGIN_ABI_VERSION:
- *
- * Note: The lower six nibbles represent the ABI version for libpurple, the
- *       rest are required by GPlugin.
- *
- * Returns: An ABI version to set in plugins using major and minor versions.
- */
-#define PURPLE_PLUGIN_ABI_VERSION(major,minor) \
-	(0x01000000 | ((major) << 16) | (minor))
-
-/**
- * PURPLE_PLUGIN_ABI_MAJOR_VERSION:
- *
- * Returns: The major version from an ABI version
- */
-#define PURPLE_PLUGIN_ABI_MAJOR_VERSION(abi) \
-	((abi >> 16) & 0xff)
-
-/**
- * PURPLE_PLUGIN_ABI_MINOR_VERSION:
- *
- * Returns: The minor version from an ABI version
- */
-#define PURPLE_PLUGIN_ABI_MINOR_VERSION(abi) \
-	(abi & 0xffff)
-
-/**
- * PURPLE_ABI_VERSION:
- *
- * A convenience‎ macro that returns an ABI version using PURPLE_MAJOR_VERSION
- * and PURPLE_MINOR_VERSION
- */
-#define PURPLE_ABI_VERSION PURPLE_PLUGIN_ABI_VERSION(PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION)
-
-/**
  * PURPLE_PLUGIN_INIT:
  *
  * Defines the plugin's entry points.
@@ -312,183 +206,6 @@
 GSList *purple_plugin_get_dependent_plugins(PurplePlugin *plugin);
 
 /**************************************************************************/
-/* PluginInfo API                                                         */
-/**************************************************************************/
-
-/**
- * purple_plugin_info_get_type:
- *
- * Returns: The #GType for the #PurplePluginInfo object.
- */
-G_DECLARE_FINAL_TYPE(PurplePluginInfo, purple_plugin_info, PURPLE, PLUGIN_INFO,
-                     GPluginPluginInfo)
-
-/**
- * purple_plugin_info_new:
- * @first_property:  The first property name
- * @...:  The value of the first property, followed optionally by more
- *             name/value pairs, followed by %NULL
- *
- * Creates a new #PurplePluginInfo instance to be returned from
- * #plugin_query of a plugin, using the provided name/value pairs.
- *
- * All properties except <literal>"id"</literal> and
- * <literal>"purple-abi"</literal> are optional.
- *
- * Valid property names are:
- * <informaltable frame='none'>
- *   <tgroup cols='2'><tbody>
- *   <row><entry><literal>"id"</literal></entry>
- *     <entry>(string) The ID of the plugin.</entry>
- *   </row>
- *   <row><entry><literal>"abi-version"</literal></entry>
- *     <entry>(<type>guint32</type>) The ABI version required by the
- *       plugin.</entry>
- *   </row>
- *   <row><entry><literal>"name"</literal></entry>
- *     <entry>(string) The translated name of the plugin.</entry>
- *   </row>
- *   <row><entry><literal>"version"</literal></entry>
- *     <entry>(string) Version of the plugin.</entry>
- *   </row>
- *   <row><entry><literal>"category"</literal></entry>
- *     <entry>(string) Primary category of the plugin.</entry>
- *   </row>
- *   <row><entry><literal>"summary"</literal></entry>
- *     <entry>(string) Brief summary of the plugin.</entry>
- *   </row>
- *   <row><entry><literal>"description"</literal></entry>
- *     <entry>(string) Full description of the plugin.</entry>
- *   </row>
- *   <row><entry><literal>"authors"</literal></entry>
- *     <entry>(<type>const gchar * const *</type>) A %NULL-terminated list of
- *       plugin authors. format: First Last &lt;user\@domain.com&gt;</entry>
- *   </row>
- *   <row><entry><literal>"website"</literal></entry>
- *     <entry>(string) Website of the plugin.</entry>
- *   </row>
- *   <row><entry><literal>"icon"</literal></entry>
- *     <entry>(string) Path to a plugin's icon.</entry>
- *   </row>
- *   <row><entry><literal>"license-id"</literal></entry>
- *     <entry>(string) Short name of the plugin's license. This should
- *       either be an identifier of the license from
- *       <ulink url="http://dep.debian.net/deps/dep5/#license-specification">
- *       DEP5</ulink> or "Other" for custom licenses.</entry>
- *   </row>
- *   <row><entry><literal>"license-text"</literal></entry>
- *     <entry>(string) The text of the plugin's license, if unlisted on
- *       DEP5.</entry>
- *   </row>
- *   <row><entry><literal>"license-url"</literal></entry>
- *     <entry>(string) The plugin's license URL, if unlisted on DEP5.</entry>
- *   </row>
- *   <row><entry><literal>"dependencies"</literal></entry>
- *     <entry>(<type>const gchar * const *</type>) A %NULL-terminated list of
- *       plugin IDs required by the plugin.</entry>
- *   </row>
- *   <row><entry><literal>"actions-cb"</literal></entry>
- *     <entry>(#PurplePluginActionsCb) Callback that returns a list of
- *       actions the plugin can perform.</entry>
- *   </row>
- *   <row><entry><literal>"extra-cb"</literal></entry>
- *     <entry>(#PurplePluginExtraCb) Callback that returns a newly
- *       allocated string denoting extra information about a plugin.</entry>
- *   </row>
- *   <row><entry><literal>"pref-frame-cb"</literal></entry>
- *     <entry>(#PurplePluginPrefFrameCb) Callback that returns a
- *       preferences frame for the plugin.</entry>
- *   </row>
- *   <row><entry><literal>"pref-request-cb"</literal></entry>
- *     <entry>(#PurplePluginPrefRequestCb) Callback that returns a
- *       preferences request handle for the plugin.</entry>
- *   </row>
- *   <row><entry><literal>"flags"</literal></entry>
- *     <entry>(#PurplePluginInfoFlags) The flags for a plugin.</entry>
- *   </row>
- *   </tbody></tgroup>
- * </informaltable>
- *
- * See #PURPLE_PLUGIN_ABI_VERSION,
- *     <link linkend="chapter-plugin-ids">Plugin IDs</link>.
- *
- * Returns: A new #PurplePluginInfo instance.
- */
-PurplePluginInfo *purple_plugin_info_new(const char *first_property, ...)
-                  G_GNUC_NULL_TERMINATED;
-
-/**
- * purple_plugin_info_get_actions_cb:
- * @info: The plugin info to get the callback from.
- *
- * Returns the callback that retrieves the list of actions a plugin can perform
- * at that moment.
- *
- * Returns: The callback that returns a list of #PurplePluginAction
- *          instances corresponding to the actions a plugin can perform.
- */
-PurplePluginActionsCb
-purple_plugin_info_get_actions_cb(PurplePluginInfo *info);
-
-/**
- * purple_plugin_info_get_extra_cb:
- * @info: The plugin info to get extra information from.
- *
- * Returns a callback that gives extra information about a plugin. You must
- * free the string returned by this callback.
- *
- * Returns: (transfer none): The callback that returns extra information about a plugin.
- */
-PurplePluginExtraCb
-purple_plugin_info_get_extra_cb(PurplePluginInfo *info);
-
-/**
- * purple_plugin_info_get_pref_frame_cb:
- * @info: The plugin info to get the callback from.
- *
- * Returns the callback that retrieves the preferences frame for a plugin, set
- * via the "pref-frame-cb" property of the plugin info.
- *
- * Returns: The callback that returns the preferences frame.
- */
-PurplePluginPrefFrameCb
-purple_plugin_info_get_pref_frame_cb(PurplePluginInfo *info);
-
-/**
- * purple_plugin_info_get_pref_request_cb:
- * @info: The plugin info to get the callback from.
- *
- * Returns the callback that retrieves the preferences request handle for a
- * plugin, set via the "pref-request-cb" property of the plugin info.
- *
- * Returns: (transfer none): The callback that returns the preferences request handle.
- */
-PurplePluginPrefRequestCb
-purple_plugin_info_get_pref_request_cb(PurplePluginInfo *info);
-
-/**
- * purple_plugin_info_get_flags:
- * @info: The plugin's info instance.
- *
- * Returns the plugin's flags.
- *
- * Returns: The flags of the plugin.
- */
-PurplePluginInfoFlags
-purple_plugin_info_get_flags(PurplePluginInfo *info);
-
-/**
- * purple_plugin_info_get_error:
- * @info: The plugin info.
- *
- * Returns an error in the plugin info that would prevent the plugin from being
- * loaded.
- *
- * Returns: The plugin info error, or %NULL.
- */
-const gchar *purple_plugin_info_get_error(PurplePluginInfo *info);
-
-/**************************************************************************/
 /* PluginAction API                                                       */
 /**************************************************************************/
 
diff --git a/libpurple/plugins.c b/libpurple/plugins.c
--- a/libpurple/plugins.c
+++ b/libpurple/plugins.c
@@ -31,49 +31,6 @@
 #include "signals.h"
 #include "util.h"
 
-typedef struct _PurplePluginInfoPrivate  PurplePluginInfoPrivate;
-
-/**************************************************************************
- * Plugin info private data
- **************************************************************************/
-struct _PurplePluginInfoPrivate {
-	char *ui_requirement;  /* ID of UI that is required to load the plugin */
-	char *error;           /* Why a plugin is not loadable                 */
-
-	PurplePluginInfoFlags flags; /* Flags for the plugin */
-
-	/* Callback that returns a list of actions the plugin can perform */
-	PurplePluginActionsCb actions_cb;
-
-	/* Callback that returns extra information about a plugin */
-	PurplePluginExtraCb extra_cb;
-
-	/* Callback that returns a preferences frame for a plugin */
-	PurplePluginPrefFrameCb pref_frame_cb;
-
-	/* Callback that returns a preferences request handle for a plugin */
-	PurplePluginPrefRequestCb pref_request_cb;
-
-	/* TRUE if a plugin has been unloaded at least once. Auto-load
-	 * plugins that have been unloaded once will not be auto-loaded again. */
-	gboolean unloaded;
-};
-
-enum
-{
-	PROP_0,
-	PROP_UI_REQUIREMENT,
-	PROP_ACTIONS_CB,
-	PROP_EXTRA_CB,
-	PROP_PREF_FRAME_CB,
-	PROP_PREF_REQUEST_CB,
-	PROP_FLAGS,
-	PROP_LAST
-};
-
-G_DEFINE_TYPE_WITH_PRIVATE(PurplePluginInfo, purple_plugin_info,
-		GPLUGIN_TYPE_PLUGIN_INFO);
-
 /**************************************************************************
  * Globals
  **************************************************************************/
@@ -88,7 +45,7 @@
                   gpointer data)
 {
 	PurplePluginInfo *info;
-	PurplePluginInfoPrivate *priv;
+	const gchar *info_error = NULL;
 
 	g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin), FALSE);
 
@@ -96,16 +53,15 @@
 	if (!info)
 		return TRUE; /* a GPlugin internal plugin */
 
-	priv = purple_plugin_info_get_instance_private(info);
-
-	if (priv->error) {
+	info_error = purple_plugin_info_get_error(info);
+	if(info_error != NULL) {
 		gchar *filename = gplugin_plugin_get_filename(plugin);
 		purple_debug_error("plugins", "Failed to load plugin %s: %s",
 		                   filename,
-		                   priv->error);
+		                   info_error);
 
 		g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0,
-				    "Plugin is not loadable: %s", priv->error);
+				    "Plugin is not loadable: %s", info_error);
 
 		g_free(filename);
 		return FALSE;
@@ -159,7 +115,6 @@
 plugin_unloaded_cb(GObject *manager, PurplePlugin *plugin)
 {
 	PurplePluginInfo *info;
-	PurplePluginInfoPrivate *priv;
 
 	g_return_if_fail(PURPLE_IS_PLUGIN(plugin));
 
@@ -167,8 +122,6 @@
 	if (!info)
 		return; /* a GPlugin internal plugin */
 
-	priv = purple_plugin_info_get_instance_private(info);
-
 	/* cancel any pending dialogs the plugin has */
 	purple_request_close_with_handle(plugin);
 	purple_notify_close_with_handle(plugin);
@@ -176,7 +129,7 @@
 	purple_signals_disconnect_by_handle(plugin);
 	purple_signals_unregister_by_instance(plugin);
 
-	priv->unloaded = TRUE;
+	purple_plugin_info_set_unloaded(info, TRUE);
 
 	loaded_plugins     = g_list_remove(loaded_plugins, plugin);
 	plugins_to_disable = g_list_remove(plugins_to_disable, plugin);
@@ -302,277 +255,6 @@
 }
 
 /**************************************************************************
- * GObject code for PurplePluginInfo
- **************************************************************************/
-/* GObject initialization function */
-static void
-purple_plugin_info_init(PurplePluginInfo *info)
-{
-}
-
-/* Set method for GObject properties */
-static void
-purple_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value,
-		GParamSpec *pspec)
-{
-	PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
-	PurplePluginInfoPrivate *priv =
-			purple_plugin_info_get_instance_private(info);
-
-	switch (param_id) {
-		case PROP_UI_REQUIREMENT:
-			priv->ui_requirement = g_value_dup_string(value);
-			break;
-		case PROP_ACTIONS_CB:
-			priv->actions_cb = g_value_get_pointer(value);
-			break;
-		case PROP_EXTRA_CB:
-			priv->extra_cb = g_value_get_pointer(value);
-			break;
-		case PROP_PREF_FRAME_CB:
-			priv->pref_frame_cb = g_value_get_pointer(value);
-			break;
-		case PROP_PREF_REQUEST_CB:
-			priv->pref_request_cb = g_value_get_pointer(value);
-			break;
-		case PROP_FLAGS:
-			priv->flags = g_value_get_flags(value);
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
-			break;
-	}
-}
-
-/* Get method for GObject properties */
-static void
-purple_plugin_info_get_property(GObject *obj, guint param_id, GValue *value,
-		GParamSpec *pspec)
-{
-	PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
-
-	switch (param_id) {
-		case PROP_ACTIONS_CB:
-			g_value_set_pointer(value,
-					purple_plugin_info_get_actions_cb(info));
-			break;
-		case PROP_EXTRA_CB:
-			g_value_set_pointer(value,
-					purple_plugin_info_get_extra_cb(info));
-			break;
-		case PROP_PREF_FRAME_CB:
-			g_value_set_pointer(value,
-					purple_plugin_info_get_pref_frame_cb(info));
-			break;
-		case PROP_PREF_REQUEST_CB:
-			g_value_set_pointer(value,
-					purple_plugin_info_get_pref_request_cb(info));
-			break;
-		case PROP_FLAGS:
-			g_value_set_flags(value, purple_plugin_info_get_flags(info));
-			break;
-		default:
-			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
-			break;
-	}
-}
-
-/* Called when done constructing */
-static void
-purple_plugin_info_constructed(GObject *object)
-{
-	PurplePluginInfo *info = PURPLE_PLUGIN_INFO(object);
-	GPluginPluginInfo *ginfo = GPLUGIN_PLUGIN_INFO(info);
-	PurplePluginInfoPrivate *priv =
-			purple_plugin_info_get_instance_private(info);
-	const char *id = gplugin_plugin_info_get_id(ginfo);
-	guint32 version;
-
-	G_OBJECT_CLASS(purple_plugin_info_parent_class)->constructed(object);
-
-	if (id == NULL || *id == '\0')
-		priv->error = g_strdup(_("This plugin has not defined an ID."));
-
-	if (priv->ui_requirement && !purple_strequal(priv->ui_requirement, purple_core_get_ui()))
-	{
-		priv->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."),
-				purple_core_get_ui(), priv->ui_requirement);
-		purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n",
-				id, priv->error);
-	}
-
-	version = gplugin_plugin_info_get_abi_version(ginfo);
-	if (PURPLE_PLUGIN_ABI_MAJOR_VERSION(version) != PURPLE_MAJOR_VERSION ||
-		PURPLE_PLUGIN_ABI_MINOR_VERSION(version) > PURPLE_MINOR_VERSION)
-	{
-		priv->error = g_strdup_printf(_("Your libpurple version is %d.%d.x (need %d.%d.x)"),
-				PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
-				PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
-				PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
-		purple_debug_error("plugins", "%s is not loadable: libpurple version is %d.%d.x (need %d.%d.x)\n",
-				id, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
-				PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
-				PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
-	}
-}
-
-/* GObject finalize function */
-static void
-purple_plugin_info_finalize(GObject *object)
-{
-	PurplePluginInfoPrivate *priv =
-			purple_plugin_info_get_instance_private(
-					PURPLE_PLUGIN_INFO(object));
-
-	g_free(priv->ui_requirement);
-	g_free(priv->error);
-
-	G_OBJECT_CLASS(purple_plugin_info_parent_class)->finalize(object);
-}
-
-/* Class initializer function */
-static void purple_plugin_info_class_init(PurplePluginInfoClass *klass)
-{
-	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
-
-	obj_class->constructed = purple_plugin_info_constructed;
-	obj_class->finalize    = purple_plugin_info_finalize;
-
-	/* Setup properties */
-	obj_class->get_property = purple_plugin_info_get_property;
-	obj_class->set_property = purple_plugin_info_set_property;
-
-	g_object_class_install_property(obj_class, PROP_UI_REQUIREMENT,
-		g_param_spec_string("ui-requirement",
-		                  "UI Requirement",
-		                  "ID of UI that is required by this plugin", NULL,
-		                  G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property(obj_class, PROP_ACTIONS_CB,
-		g_param_spec_pointer("actions-cb",
-		                  "Plugin actions",
-		                  "Callback that returns list of plugin's actions",
-		                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                  G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property(obj_class, PROP_EXTRA_CB,
-		g_param_spec_pointer("extra-cb",
-		                  "Extra info callback",
-		                  "Callback that returns extra info about the plugin",
-		                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                  G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property(obj_class, PROP_PREF_FRAME_CB,
-		g_param_spec_pointer("pref-frame-cb",
-		                  "Preferences frame callback",
-		                  "The callback that returns the preferences frame",
-		                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                  G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property(obj_class, PROP_PREF_REQUEST_CB,
-		g_param_spec_pointer("pref-request-cb",
-		                  "Preferences request callback",
-		                  "Callback that returns preferences request handle",
-		                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                  G_PARAM_STATIC_STRINGS));
-
-	g_object_class_install_property(obj_class, PROP_FLAGS,
-		g_param_spec_flags("flags",
-		                  "Plugin flags",
-		                  "The flags for the plugin",
-		                  PURPLE_TYPE_PLUGIN_INFO_FLAGS, 0,
-		                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-		                  G_PARAM_STATIC_STRINGS));
-}
-
-/**************************************************************************
- * PluginInfo API
- **************************************************************************/
-PurplePluginInfo *
-purple_plugin_info_new(const char *first_property, ...)
-{
-	GObject *info;
-	va_list var_args;
-
-	/* at least ID is required */
-	if (!first_property)
-		return NULL;
-
-	va_start(var_args, first_property);
-	info = g_object_new_valist(PURPLE_TYPE_PLUGIN_INFO, first_property,
-	                           var_args);
-	va_end(var_args);
-
-	return PURPLE_PLUGIN_INFO(info);
-}
-
-PurplePluginActionsCb
-purple_plugin_info_get_actions_cb(PurplePluginInfo *info)
-{
-	PurplePluginInfoPrivate *priv = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
-
-	priv = purple_plugin_info_get_instance_private(info);
-	return priv->actions_cb;
-}
-
-PurplePluginExtraCb
-purple_plugin_info_get_extra_cb(PurplePluginInfo *info)
-{
-	PurplePluginInfoPrivate *priv = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
-
-	priv = purple_plugin_info_get_instance_private(info);
-	return priv->extra_cb;
-}
-
-PurplePluginPrefFrameCb
-purple_plugin_info_get_pref_frame_cb(PurplePluginInfo *info)
-{
-	PurplePluginInfoPrivate *priv = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
-
-	priv = purple_plugin_info_get_instance_private(info);
-	return priv->pref_frame_cb;
-}
-
-PurplePluginPrefRequestCb
-purple_plugin_info_get_pref_request_cb(PurplePluginInfo *info)
-{
-	PurplePluginInfoPrivate *priv = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
-
-	priv = purple_plugin_info_get_instance_private(info);
-	return priv->pref_request_cb;
-}
-
-PurplePluginInfoFlags
-purple_plugin_info_get_flags(PurplePluginInfo *info)
-{
-	PurplePluginInfoPrivate *priv = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), 0);
-
-	priv = purple_plugin_info_get_instance_private(info);
-	return priv->flags;
-}
-
-const gchar *
-purple_plugin_info_get_error(PurplePluginInfo *info)
-{
-	PurplePluginInfoPrivate *priv = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
-
-	priv = purple_plugin_info_get_instance_private(info);
-	return priv->error;
-}
-
-/**************************************************************************
  * PluginAction API
  **************************************************************************/
 PurplePluginAction *
@@ -660,16 +342,17 @@
 	for (l = plugins; l != NULL; l = l->next) {
 		PurplePlugin *plugin = PURPLE_PLUGIN(l->data);
 		PurplePluginInfo *info;
-		PurplePluginInfoPrivate *priv;
+		PurplePluginInfoFlags flags;
+		gboolean unloaded;
 
 		if (purple_plugin_is_loaded(plugin))
 			continue;
 
 		info = purple_plugin_get_info(plugin);
-		priv = purple_plugin_info_get_instance_private(info);
 
-		if (!priv->unloaded && purple_plugin_info_get_flags(info) &
-				PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD) {
+		unloaded = purple_plugin_info_get_unloaded(info);
+		flags = purple_plugin_info_get_flags(info);
+		if (!unloaded && flags & PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD) {
 			gchar *filename = gplugin_plugin_get_filename(plugin);
 			purple_debug_info("plugins", "Auto-loading plugin %s\n",
 			                  filename);
diff --git a/libpurple/purpleplugininfo.h b/libpurple/purpleplugininfo.h
new file mode 100644
--- /dev/null
+++ b/libpurple/purpleplugininfo.h
@@ -0,0 +1,364 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(PURPLE_GLOBAL_HEADER_INSIDE) && !defined(PURPLE_COMPILATION)
+# error "only <purple.h> may be included directly"
+#endif
+
+#ifndef PURPLE_PLUGIN_INFO_H
+#define PURPLE_PLUGIN_INFO_H
+
+/**
+ * SECTION:purpleplugininfo
+ * @section_id: libpurple-purpleplugininfo
+ * @title: PurplePluginInfo Object
+ * @see_also: <link linkend="chapter-signals-plugin">Plugin signals</link>,
+ *     <link linkend="chapter-plugin-ids">Plugin IDs</link>,
+ *     <link linkend="chapter-plugin-i18n">Third Party Plugin Translation</link>
+ */
+
+#include <glib.h>
+
+#include <gplugin.h>
+#include <gplugin-native.h>
+
+#include "pluginpref.h"
+
+/**
+ * PURPLE_TYPE_PLUGIN_INFO:
+ *
+ * The standard _get_type macro for #PurplePluginInfo.
+ */
+#define PURPLE_TYPE_PLUGIN_INFO (purple_plugin_info_get_type())
+
+/**
+ * purple_plugin_info_get_type:
+ *
+ * Returns: The #GType for the #PurplePluginInfo object.
+ */
+G_DECLARE_DERIVABLE_TYPE(PurplePluginInfo, purple_plugin_info, PURPLE, PLUGIN_INFO,
+                         GPluginPluginInfo)
+
+#include "plugins.h"
+
+struct _PurplePluginInfoClass {
+	/*< private >*/
+	GPluginPluginInfoClass parent;
+
+	gpointer reserved[4];
+};
+
+/**
+ * PurplePluginActionsCb:
+ * @plugin: the plugin associated with this callback.
+ *
+ * Returns a list of actions the plugin can perform.
+ *
+ * Returns: (transfer none): A list of actions the plugin can perform.
+ */
+typedef GList *(*PurplePluginActionsCb)(PurplePlugin *plugin);
+
+/**
+ * PurplePluginExtraCb:
+ * @plugin: the plugin associated with this callback.
+ *
+ * Gives extra information about the plguin.
+ *
+ * Returns: a newly allocated string denoting extra information
+ * about a plugin.
+ */
+typedef gchar *(*PurplePluginExtraCb)(PurplePlugin *plugin);
+
+/**
+ * PurplePluginPrefFrameCb:
+ * @plugin: the plugin associated with this callback.
+ *
+ * Returns the preferences frame for the plugin.
+ *
+ * Returns: Preference frame.
+ */
+typedef PurplePluginPrefFrame *(*PurplePluginPrefFrameCb)(PurplePlugin *plugin);
+
+/**
+ * PurplePrefRequestCb:
+ *
+ * Returns the preferences request handle for a plugin.
+ *
+ * Returns: Preferences request handle.
+ */
+typedef gpointer (*PurplePluginPrefRequestCb)(PurplePlugin *plugin);
+
+/**
+ * PurplePluginInfoFlags:
+ * @PURPLE_PLUGIN_INFO_FLAGS_INTERNAL:  Plugin is not shown in UI lists
+ * @PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD: Auto-load the plugin
+ *
+ * Flags that can be used to treat plugins differently.
+ */
+typedef enum /*< flags >*/
+{
+	PURPLE_PLUGIN_INFO_FLAGS_INTERNAL  = 1 << 1,
+	PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD = 1 << 2,
+
+} PurplePluginInfoFlags;
+
+/**
+ * PurplePluginInfo:
+ *
+ * Holds information about a plugin.
+ *
+ * Since: 3.0.0
+ */
+
+/**
+ * PURPLE_PLUGIN_ABI_VERSION:
+ *
+ * Note: The lower six nibbles represent the ABI version for libpurple, the
+ *       rest are required by GPlugin.
+ *
+ * Returns: An ABI version to set in plugins using major and minor versions.
+ */
+#define PURPLE_PLUGIN_ABI_VERSION(major,minor) \
+	(0x01000000 | ((major) << 16) | (minor))
+
+/**
+ * PURPLE_PLUGIN_ABI_MAJOR_VERSION:
+ *
+ * Returns: The major version from an ABI version
+ */
+#define PURPLE_PLUGIN_ABI_MAJOR_VERSION(abi) \
+	((abi >> 16) & 0xff)
+
+/**
+ * PURPLE_PLUGIN_ABI_MINOR_VERSION:
+ *
+ * Returns: The minor version from an ABI version
+ */
+#define PURPLE_PLUGIN_ABI_MINOR_VERSION(abi) \
+	(abi & 0xffff)
+
+/**
+ * PURPLE_ABI_VERSION:
+ *
+ * A convenience macro that returns an ABI version using PURPLE_MAJOR_VERSION
+ * and PURPLE_MINOR_VERSION
+ */
+#define PURPLE_ABI_VERSION PURPLE_PLUGIN_ABI_VERSION(PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION)
+
+G_BEGIN_DECLS
+
+/**
+ * purple_plugin_info_new:
+ * @first_property:  The first property name
+ * @...:  The value of the first property, followed optionally by more
+ *             name/value pairs, followed by %NULL
+ *
+ * Creates a new #PurplePluginInfo instance to be returned from
+ * #plugin_query of a plugin, using the provided name/value pairs.
+ *
+ * All properties except <literal>"id"</literal> and
+ * <literal>"purple-abi"</literal> are optional.
+ *
+ * Valid property names are:
+ * <informaltable frame='none'>
+ *   <tgroup cols='2'><tbody>
+ *   <row><entry><literal>"id"</literal></entry>
+ *     <entry>(string) The ID of the plugin.</entry>
+ *   </row>
+ *   <row><entry><literal>"abi-version"</literal></entry>
+ *     <entry>(<type>guint32</type>) The ABI version required by the
+ *       plugin.</entry>
+ *   </row>
+ *   <row><entry><literal>"name"</literal></entry>
+ *     <entry>(string) The translated name of the plugin.</entry>
+ *   </row>
+ *   <row><entry><literal>"version"</literal></entry>
+ *     <entry>(string) Version of the plugin.</entry>
+ *   </row>
+ *   <row><entry><literal>"category"</literal></entry>
+ *     <entry>(string) Primary category of the plugin.</entry>
+ *   </row>
+ *   <row><entry><literal>"summary"</literal></entry>
+ *     <entry>(string) Brief summary of the plugin.</entry>
+ *   </row>
+ *   <row><entry><literal>"description"</literal></entry>
+ *     <entry>(string) Full description of the plugin.</entry>
+ *   </row>
+ *   <row><entry><literal>"authors"</literal></entry>
+ *     <entry>(<type>const gchar * const *</type>) A %NULL-terminated list of
+ *       plugin authors. format: First Last &lt;user\@domain.com&gt;</entry>
+ *   </row>
+ *   <row><entry><literal>"website"</literal></entry>
+ *     <entry>(string) Website of the plugin.</entry>
+ *   </row>
+ *   <row><entry><literal>"icon"</literal></entry>
+ *     <entry>(string) Path to a plugin's icon.</entry>
+ *   </row>
+ *   <row><entry><literal>"license-id"</literal></entry>
+ *     <entry>(string) Short name of the plugin's license. This should
+ *       either be an identifier of the license from
+ *       <ulink url="http://dep.debian.net/deps/dep5/#license-specification">
+ *       DEP5</ulink> or "Other" for custom licenses.</entry>
+ *   </row>
+ *   <row><entry><literal>"license-text"</literal></entry>
+ *     <entry>(string) The text of the plugin's license, if unlisted on
+ *       DEP5.</entry>
+ *   </row>
+ *   <row><entry><literal>"license-url"</literal></entry>
+ *     <entry>(string) The plugin's license URL, if unlisted on DEP5.</entry>
+ *   </row>
+ *   <row><entry><literal>"dependencies"</literal></entry>
+ *     <entry>(<type>const gchar * const *</type>) A %NULL-terminated list of
+ *       plugin IDs required by the plugin.</entry>
+ *   </row>
+ *   <row><entry><literal>"actions-cb"</literal></entry>
+ *     <entry>(#PurplePluginActionsCb) Callback that returns a list of
+ *       actions the plugin can perform.</entry>
+ *   </row>
+ *   <row><entry><literal>"extra-cb"</literal></entry>
+ *     <entry>(#PurplePluginExtraCb) Callback that returns a newly
+ *       allocated string denoting extra information about a plugin.</entry>
+ *   </row>
+ *   <row><entry><literal>"pref-frame-cb"</literal></entry>
+ *     <entry>(#PurplePluginPrefFrameCb) Callback that returns a
+ *       preferences frame for the plugin.</entry>
+ *   </row>
+ *   <row><entry><literal>"pref-request-cb"</literal></entry>
+ *     <entry>(#PurplePluginPrefRequestCb) Callback that returns a
+ *       preferences request handle for the plugin.</entry>
+ *   </row>
+ *   <row><entry><literal>"flags"</literal></entry>
+ *     <entry>(#PurplePluginInfoFlags) The flags for a plugin.</entry>
+ *   </row>
+ *   </tbody></tgroup>
+ * </informaltable>
+ *
+ * See #PURPLE_PLUGIN_ABI_VERSION,
+ *     <link linkend="chapter-plugin-ids">Plugin IDs</link>.
+ *
+ * Returns: A new #PurplePluginInfo instance.
+ *
+ * Since: 3.0.0
+ */
+PurplePluginInfo *purple_plugin_info_new(const char *first_property, ...) G_GNUC_NULL_TERMINATED;
+
+/**
+ * purple_plugin_info_get_actions_cb:
+ * @info: The plugin info to get the callback from.
+ *
+ * Returns the callback that retrieves the list of actions a plugin can perform
+ * at that moment.
+ *
+ * Returns: The callback that returns a list of #PurplePluginAction
+ *          instances corresponding to the actions a plugin can perform.
+ *
+ * Since: 3.0.0
+ */
+PurplePluginActionsCb purple_plugin_info_get_actions_cb(PurplePluginInfo *info);
+
+/**
+ * purple_plugin_info_get_extra_cb:
+ * @info: The plugin info to get extra information from.
+ *
+ * Returns a callback that gives extra information about a plugin. You must
+ * free the string returned by this callback.
+ *
+ * Returns: (transfer none): The callback that returns extra information about a plugin.
+ *
+ * Since: 3.0.0
+ */
+PurplePluginExtraCb purple_plugin_info_get_extra_cb(PurplePluginInfo *info);
+
+/**
+ * purple_plugin_info_get_pref_frame_cb:
+ * @info: The plugin info to get the callback from.
+ *
+ * Returns the callback that retrieves the preferences frame for a plugin, set
+ * via the "pref-frame-cb" property of the plugin info.
+ *
+ * Returns: The callback that returns the preferences frame.
+ *
+ * Since: 3.0.0
+ */
+PurplePluginPrefFrameCb purple_plugin_info_get_pref_frame_cb(PurplePluginInfo *info);
+
+/**
+ * purple_plugin_info_get_pref_request_cb:
+ * @info: The plugin info to get the callback from.
+ *
+ * Returns the callback that retrieves the preferences request handle for a
+ * plugin, set via the "pref-request-cb" property of the plugin info.
+ *
+ * Returns: (transfer none): The callback that returns the preferences request handle.
+ *
+ * Since: 3.0.0
+ */
+PurplePluginPrefRequestCb purple_plugin_info_get_pref_request_cb(PurplePluginInfo *info);
+
+/**
+ * purple_plugin_info_get_flags:
+ * @info: The plugin's info instance.
+ *
+ * Returns the plugin's flags.
+ *
+ * Returns: The flags of the plugin.
+ *
+ * Since: 3.0.0
+ */
+PurplePluginInfoFlags purple_plugin_info_get_flags(PurplePluginInfo *info);
+
+/**
+ * purple_plugin_info_get_error:
+ * @info: The plugin info.
+ *
+ * Returns an error in the plugin info that would prevent the plugin from being
+ * loaded.
+ *
+ * Returns: The plugin info error, or %NULL.
+ *
+ * Since: 3.0.0
+ */
+const gchar *purple_plugin_info_get_error(PurplePluginInfo *info);
+
+/**
+ * purple_plugin_info_get_unloaded:
+ * @info: The #PurplePluginInfo instance.
+ *
+ * Gets whether or not the plugin has been unloaded.
+ *
+ * Returns: %TRUE if the plugin has been unloaded previously, or %FALSE if not.
+ *
+ * Since: 3.0.0
+ */
+gboolean purple_plugin_info_get_unloaded(PurplePluginInfo *info);
+
+/**
+ * purple_plugin_info_set_unloaded:
+ * @info: The #PurplePluginInfo instance.
+ * @unloaded: %TRUE to say the plugin has been unloaded.
+ *
+ * Sets the unloaded state of @info to @unloaded.
+ *
+ * Since: 3.0.0
+ */
+void purple_plugin_info_set_unloaded(PurplePluginInfo *info, gboolean unloaded);
+
+G_END_DECLS
+
+#endif /* PURPLE_PLUGIN_INFO_H */
+
diff --git a/libpurple/purpleplugininfo.c b/libpurple/purpleplugininfo.c
new file mode 100644
--- /dev/null
+++ b/libpurple/purpleplugininfo.c
@@ -0,0 +1,358 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+#include <glib/gi18n-lib.h>
+
+#include "internal.h"
+
+#include "core.h"
+#include "debug.h"
+#include "enums.h"
+#include "util.h"
+
+#include "purpleplugininfo.h"
+
+typedef struct {
+	char *ui_requirement;  /* ID of UI that is required to load the plugin */
+	char *error;           /* Why a plugin is not loadable                 */
+
+	PurplePluginInfoFlags flags; /* Flags for the plugin */
+
+	/* Callback that returns a list of actions the plugin can perform */
+	PurplePluginActionsCb actions_cb;
+
+	/* Callback that returns extra information about a plugin */
+	PurplePluginExtraCb extra_cb;
+
+	/* Callback that returns a preferences frame for a plugin */
+	PurplePluginPrefFrameCb pref_frame_cb;
+
+	/* Callback that returns a preferences request handle for a plugin */
+	PurplePluginPrefRequestCb pref_request_cb;
+
+	/* TRUE if a plugin has been unloaded at least once. Auto-load
+	 * plugins that have been unloaded once will not be auto-loaded again. */
+	gboolean unloaded;
+} PurplePluginInfoPrivate;
+
+enum {
+	PROP_0,
+	PROP_UI_REQUIREMENT,
+	PROP_ACTIONS_CB,
+	PROP_EXTRA_CB,
+	PROP_PREF_FRAME_CB,
+	PROP_PREF_REQUEST_CB,
+	PROP_FLAGS,
+	PROP_LAST
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(PurplePluginInfo, purple_plugin_info,
+                           GPLUGIN_TYPE_PLUGIN_INFO);
+
+/**************************************************************************
+ * GObject code for PurplePluginInfo
+ **************************************************************************/
+/* GObject initialization function */
+static void
+purple_plugin_info_init(PurplePluginInfo *info)
+{
+}
+
+/* Set method for GObject properties */
+static void
+purple_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value,
+		GParamSpec *pspec)
+{
+	PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
+	PurplePluginInfoPrivate *priv =
+			purple_plugin_info_get_instance_private(info);
+
+	switch (param_id) {
+		case PROP_UI_REQUIREMENT:
+			priv->ui_requirement = g_value_dup_string(value);
+			break;
+		case PROP_ACTIONS_CB:
+			priv->actions_cb = g_value_get_pointer(value);
+			break;
+		case PROP_EXTRA_CB:
+			priv->extra_cb = g_value_get_pointer(value);
+			break;
+		case PROP_PREF_FRAME_CB:
+			priv->pref_frame_cb = g_value_get_pointer(value);
+			break;
+		case PROP_PREF_REQUEST_CB:
+			priv->pref_request_cb = g_value_get_pointer(value);
+			break;
+		case PROP_FLAGS:
+			priv->flags = g_value_get_flags(value);
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+/* Get method for GObject properties */
+static void
+purple_plugin_info_get_property(GObject *obj, guint param_id, GValue *value,
+		GParamSpec *pspec)
+{
+	PurplePluginInfo *info = PURPLE_PLUGIN_INFO(obj);
+
+	switch (param_id) {
+		case PROP_ACTIONS_CB:
+			g_value_set_pointer(value,
+					purple_plugin_info_get_actions_cb(info));
+			break;
+		case PROP_EXTRA_CB:
+			g_value_set_pointer(value,
+					purple_plugin_info_get_extra_cb(info));
+			break;
+		case PROP_PREF_FRAME_CB:
+			g_value_set_pointer(value,
+					purple_plugin_info_get_pref_frame_cb(info));
+			break;
+		case PROP_PREF_REQUEST_CB:
+			g_value_set_pointer(value,
+					purple_plugin_info_get_pref_request_cb(info));
+			break;
+		case PROP_FLAGS:
+			g_value_set_flags(value, purple_plugin_info_get_flags(info));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+/* Called when done constructing */
+static void
+purple_plugin_info_constructed(GObject *object)
+{
+	PurplePluginInfo *info = PURPLE_PLUGIN_INFO(object);
+	GPluginPluginInfo *ginfo = GPLUGIN_PLUGIN_INFO(info);
+	PurplePluginInfoPrivate *priv =
+			purple_plugin_info_get_instance_private(info);
+	const char *id = gplugin_plugin_info_get_id(ginfo);
+	guint32 version;
+
+	G_OBJECT_CLASS(purple_plugin_info_parent_class)->constructed(object);
+
+	if (id == NULL || *id == '\0')
+		priv->error = g_strdup(_("This plugin has not defined an ID."));
+
+	if (priv->ui_requirement && !purple_strequal(priv->ui_requirement, purple_core_get_ui()))
+	{
+		priv->error = g_strdup_printf(_("You are using %s, but this plugin requires %s."),
+				purple_core_get_ui(), priv->ui_requirement);
+		purple_debug_error("plugins", "%s is not loadable: The UI requirement is not met. (%s)\n",
+				id, priv->error);
+	}
+
+	version = gplugin_plugin_info_get_abi_version(ginfo);
+	if (PURPLE_PLUGIN_ABI_MAJOR_VERSION(version) != PURPLE_MAJOR_VERSION ||
+		PURPLE_PLUGIN_ABI_MINOR_VERSION(version) > PURPLE_MINOR_VERSION)
+	{
+		priv->error = g_strdup_printf(_("Your libpurple version is %d.%d.x (need %d.%d.x)"),
+				PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
+				PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
+				PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
+		purple_debug_error("plugins", "%s is not loadable: libpurple version is %d.%d.x (need %d.%d.x)\n",
+				id, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION,
+				PURPLE_PLUGIN_ABI_MAJOR_VERSION(version),
+				PURPLE_PLUGIN_ABI_MINOR_VERSION(version));
+	}
+}
+
+/* GObject finalize function */
+static void
+purple_plugin_info_finalize(GObject *object)
+{
+	PurplePluginInfoPrivate *priv =
+			purple_plugin_info_get_instance_private(
+					PURPLE_PLUGIN_INFO(object));
+
+	g_free(priv->ui_requirement);
+	g_free(priv->error);
+
+	G_OBJECT_CLASS(purple_plugin_info_parent_class)->finalize(object);
+}
+
+/* Class initializer function */
+static void purple_plugin_info_class_init(PurplePluginInfoClass *klass)
+{
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	obj_class->constructed = purple_plugin_info_constructed;
+	obj_class->finalize    = purple_plugin_info_finalize;
+
+	/* Setup properties */
+	obj_class->get_property = purple_plugin_info_get_property;
+	obj_class->set_property = purple_plugin_info_set_property;
+
+	g_object_class_install_property(obj_class, PROP_UI_REQUIREMENT,
+		g_param_spec_string("ui-requirement",
+		                  "UI Requirement",
+		                  "ID of UI that is required by this plugin", NULL,
+		                  G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property(obj_class, PROP_ACTIONS_CB,
+		g_param_spec_pointer("actions-cb",
+		                  "Plugin actions",
+		                  "Callback that returns list of plugin's actions",
+		                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                  G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property(obj_class, PROP_EXTRA_CB,
+		g_param_spec_pointer("extra-cb",
+		                  "Extra info callback",
+		                  "Callback that returns extra info about the plugin",
+		                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                  G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property(obj_class, PROP_PREF_FRAME_CB,
+		g_param_spec_pointer("pref-frame-cb",
+		                  "Preferences frame callback",
+		                  "The callback that returns the preferences frame",
+		                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                  G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property(obj_class, PROP_PREF_REQUEST_CB,
+		g_param_spec_pointer("pref-request-cb",
+		                  "Preferences request callback",
+		                  "Callback that returns preferences request handle",
+		                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                  G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property(obj_class, PROP_FLAGS,
+		g_param_spec_flags("flags",
+		                  "Plugin flags",
+		                  "The flags for the plugin",
+		                  PURPLE_TYPE_PLUGIN_INFO_FLAGS, 0,
+		                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+		                  G_PARAM_STATIC_STRINGS));
+}
+
+/**************************************************************************
+ * PluginInfo API
+ **************************************************************************/
+PurplePluginInfo *
+purple_plugin_info_new(const char *first_property, ...)
+{
+	GObject *info;
+	va_list var_args;
+
+	/* at least ID is required */
+	if (!first_property)
+		return NULL;
+
+	va_start(var_args, first_property);
+	info = g_object_new_valist(PURPLE_TYPE_PLUGIN_INFO, first_property,
+	                           var_args);
+	va_end(var_args);
+
+	return PURPLE_PLUGIN_INFO(info);
+}
+
+PurplePluginActionsCb
+purple_plugin_info_get_actions_cb(PurplePluginInfo *info)
+{
+	PurplePluginInfoPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
+
+	priv = purple_plugin_info_get_instance_private(info);
+	return priv->actions_cb;
+}
+
+PurplePluginExtraCb
+purple_plugin_info_get_extra_cb(PurplePluginInfo *info)
+{
+	PurplePluginInfoPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
+
+	priv = purple_plugin_info_get_instance_private(info);
+	return priv->extra_cb;
+}
+
+PurplePluginPrefFrameCb
+purple_plugin_info_get_pref_frame_cb(PurplePluginInfo *info)
+{
+	PurplePluginInfoPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
+
+	priv = purple_plugin_info_get_instance_private(info);
+	return priv->pref_frame_cb;
+}
+
+PurplePluginPrefRequestCb
+purple_plugin_info_get_pref_request_cb(PurplePluginInfo *info)
+{
+	PurplePluginInfoPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
+
+	priv = purple_plugin_info_get_instance_private(info);
+	return priv->pref_request_cb;
+}
+
+PurplePluginInfoFlags
+purple_plugin_info_get_flags(PurplePluginInfo *info)
+{
+	PurplePluginInfoPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), 0);
+
+	priv = purple_plugin_info_get_instance_private(info);
+	return priv->flags;
+}
+
+const gchar *
+purple_plugin_info_get_error(PurplePluginInfo *info)
+{
+	PurplePluginInfoPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
+
+	priv = purple_plugin_info_get_instance_private(info);
+	return priv->error;
+}
+
+gboolean
+purple_plugin_info_get_unloaded(PurplePluginInfo *info) {
+	PurplePluginInfoPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), FALSE);
+
+	priv = purple_plugin_info_get_instance_private(info);
+
+	return priv->unloaded;
+}
+
+void
+purple_plugin_info_set_unloaded(PurplePluginInfo *info, gboolean unloaded) {
+	PurplePluginInfoPrivate *priv = NULL;
+
+	g_return_if_fail(PURPLE_IS_PLUGIN_INFO(info));
+
+	priv = purple_plugin_info_get_instance_private(info);
+
+	priv->unloaded = unloaded;
+}
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -253,6 +253,7 @@
 libpurple/purplenoopcredentialprovider.c
 libpurple/purpleoptions.c
 libpurple/purplepresence.c
+libpurple/purpleplugininfo.c
 libpurple/purpleprotocolattention.c
 libpurple/purpleprotocolchat.c
 libpurple/purpleprotocolclient.c
