diff --git a/finch/gntaccount.c b/finch/gntaccount.c
--- a/finch/gntaccount.c
+++ b/finch/gntaccount.c
@@ -117,8 +117,9 @@
 
 	if (protocol != NULL)
 	{
-		GList *iter, *entries;
-		for (iter = purple_protocol_get_user_splits(protocol), entries = dialog->split_entries;
+		GList *iter, *entries, *splits;
+		splits = purple_protocol_get_user_splits(protocol);
+		for (iter = splits, entries = dialog->split_entries;
 				iter && entries; iter = iter->next, entries = entries->next)
 		{
 			PurpleAccountUserSplit *split = iter->data;
@@ -131,6 +132,8 @@
 					purple_account_user_split_get_separator(split),
 					value);
 		}
+		g_list_free_full(splits,
+		                 (GDestroyNotify)purple_account_user_split_destroy);
 	}
 
 	if (dialog->account == NULL)
@@ -199,9 +202,10 @@
 	/* Protocol options */
 	if (protocol)
 	{
-		GList *iter, *entries;
+		GList *iter, *entries, *opts;
 
-		for (iter = purple_protocol_get_account_options(protocol), entries = dialog->protocol_entries;
+		opts = purple_protocol_get_account_options(protocol);
+		for (iter = opts, entries = dialog->protocol_entries;
 				iter && entries; iter = iter->next, entries = entries->next)
 		{
 			PurpleAccountOption *option = iter->data;
@@ -237,6 +241,7 @@
 				g_assert_not_reached();
 			}
 		}
+		g_list_free_full(opts, (GDestroyNotify)purple_account_option_destroy);
 	}
 
 	/* XXX: Proxy options */
@@ -279,7 +284,7 @@
 {
 	GntWidget *hbox;
 	PurpleProtocol *protocol;
-	GList *iter, *entries;
+	GList *iter, *entries, *splits;
 	char *username = NULL;
 
 	if (dialog->splits)
@@ -302,7 +307,8 @@
 
 	username = dialog->account ? g_strdup(purple_account_get_username(dialog->account)) : NULL;
 
-	for (iter = purple_protocol_get_user_splits(protocol); iter; iter = iter->next)
+	splits = purple_protocol_get_user_splits(protocol);
+	for (iter = splits; iter; iter = iter->next)
 	{
 		PurpleAccountUserSplit *split = iter->data;
 		GntWidget *entry = NULL;
@@ -323,7 +329,7 @@
 		g_free(buf);
 	}
 
-	for (iter = g_list_last(purple_protocol_get_user_splits(protocol)), entries = g_list_last(dialog->split_entries);
+	for (iter = g_list_last(splits), entries = g_list_last(dialog->split_entries);
 			iter && entries; iter = iter->prev, entries = entries->prev)
 	{
 		GntWidget *entry = entries->data;
@@ -352,6 +358,8 @@
 			gnt_entry_set_text(GNT_ENTRY(entry), value);
 	}
 
+	g_list_free_full(splits, (GDestroyNotify)purple_account_user_split_destroy);
+
 	if (username != NULL)
 		gnt_entry_set_text(GNT_ENTRY(dialog->username), username);
 
@@ -362,7 +370,7 @@
 add_account_options(AccountEditDialog *dialog)
 {
 	PurpleProtocol *protocol;
-	GList *iter;
+	GList *iter, *opts;
 	GntWidget *vbox, *box;
 	PurpleAccount *account;
 
@@ -390,7 +398,8 @@
 
 	account = dialog->account;
 
-	for (iter = purple_protocol_get_account_options(protocol); iter; iter = iter->next)
+	opts = purple_protocol_get_account_options(protocol);
+	for (iter = opts; iter; iter = iter->next)
 	{
 		PurpleAccountOption *option = iter->data;
 		PurplePrefType type = purple_account_option_get_pref_type(option);
@@ -477,6 +486,7 @@
 			}
 		}
 	}
+	g_list_free_full(opts, (GDestroyNotify)purple_account_option_destroy);
 
 	/* Show the registration checkbox only in a new account dialog,
 	 * and when the selected protocol has the support for it. */
diff --git a/libpurple/account.c b/libpurple/account.c
--- a/libpurple/account.c
+++ b/libpurple/account.c
@@ -1015,7 +1015,7 @@
 	}
 
 	purple_account_set_status_types(account,
-			purple_protocol_class_status_types(protocol, account));
+			purple_protocol_status_types(protocol, account));
 
 	priv->presence = PURPLE_PRESENCE(purple_account_presence_new(account));
 
diff --git a/libpurple/buddyicon.h b/libpurple/buddyicon.h
--- a/libpurple/buddyicon.h
+++ b/libpurple/buddyicon.h
@@ -491,6 +491,16 @@
 		PurpleBuddyIconScaleFlags scale_rules);
 
 /**
+ * purple_buddy_icon_spec_free:
+ * @spec: The #PurpleBuddyIconSpec instance.
+ *
+ * Free's @spec.
+ *
+ * Since: 3.0.0
+ */
+void purple_buddy_icon_spec_free(PurpleBuddyIconSpec *spec);
+
+/**
  * purple_buddy_icon_spec_get_scaled_size:
  * @spec: The buddy icon spec.
  * @width: (inout): On input, the suggested width. On output, the width
diff --git a/libpurple/buddyicon.c b/libpurple/buddyicon.c
--- a/libpurple/buddyicon.c
+++ b/libpurple/buddyicon.c
@@ -1143,6 +1143,15 @@
 	return icon_spec;
 }
 
+void
+purple_buddy_icon_spec_free(PurpleBuddyIconSpec *spec) {
+	if(spec == NULL) {
+		return;
+	}
+
+	g_free(spec);
+}
+
 static PurpleBuddyIconSpec *
 purple_buddy_icon_spec_copy(PurpleBuddyIconSpec *icon_spec)
 {
diff --git a/libpurple/connection.c b/libpurple/connection.c
--- a/libpurple/connection.c
+++ b/libpurple/connection.c
@@ -886,7 +886,7 @@
 
 	update_keepalive(gc, FALSE);
 
-	purple_protocol_class_close(priv->protocol, gc);
+	purple_protocol_close(priv->protocol, gc);
 
 	/* Clear out the proto data that was freed in the protocol's close method */
 	buddies = purple_blist_find_buddies(account, NULL);
@@ -1048,7 +1048,7 @@
 		purple_debug_info("connection", "Connecting. gc = %p\n", gc);
 
 		purple_signal_emit(purple_accounts_get_handle(), "account-connecting", account);
-		purple_protocol_class_login(protocol, account);
+		purple_protocol_login(protocol, account);
 	}
 }
 
diff --git a/libpurple/log.c b/libpurple/log.c
--- a/libpurple/log.c
+++ b/libpurple/log.c
@@ -287,7 +287,7 @@
 	if (!protocol)
 		return NULL;
 
-	protocol_name = purple_protocol_class_list_icon(protocol, account, NULL);
+	protocol_name = purple_protocol_list_icon(protocol, account, NULL);
 
 	acct_name = g_strdup(purple_escape_filename(purple_normalize(account,
 				purple_account_get_username(account))));
@@ -996,7 +996,7 @@
 			if (!protocol)
 				continue;
 
-			if (purple_strequal(protocol_unescaped, purple_protocol_class_list_icon(protocol, (PurpleAccount *)account_iter->data, NULL)))
+			if (purple_strequal(protocol_unescaped, purple_protocol_list_icon(protocol, (PurpleAccount *)account_iter->data, NULL)))
 				accounts = g_list_prepend(accounts, account_iter->data);
 		}
 		g_free(protocol_unescaped);
@@ -1182,7 +1182,7 @@
 	gsize written = 0;
 
 	if(!data) {
-		const char *proto = purple_protocol_class_list_icon(protocol, log->account, NULL);
+		const char *proto = purple_protocol_list_icon(protocol, log->account, NULL);
 		GDateTime *dt;
 		gchar *date;
 		purple_log_common_writer(log, ".html");
@@ -1344,7 +1344,7 @@
 		 * creating a new file there would result in empty files in the case
 		 * that you open a convo with someone, but don't say anything.
 		 */
-		const char *proto = purple_protocol_class_list_icon(protocol, log->account, NULL);
+		const char *proto = purple_protocol_list_icon(protocol, log->account, NULL);
 		GDateTime *dt;
 		gchar *date;
 		purple_log_common_writer(log, ".txt");
diff --git a/libpurple/plugins/log_reader.c b/libpurple/plugins/log_reader.c
--- a/libpurple/plugins/log_reader.c
+++ b/libpurple/plugins/log_reader.c
@@ -79,7 +79,7 @@
 	if (!protocol)
 		return NULL;
 
-	protocol_name = g_ascii_strup(purple_protocol_class_list_icon(protocol, account, NULL), -1);
+	protocol_name = g_ascii_strup(purple_protocol_list_icon(protocol, account, NULL), -1);
 
 	temp = g_strdup_printf("%s.%s", protocol_name, purple_account_get_username(account));
 	path = g_build_filename(logdir, temp, sn, NULL);
@@ -1090,7 +1090,7 @@
 	if (!protocol)
 		return NULL;
 
-	protocol_name = g_ascii_strup(purple_protocol_class_list_icon(protocol, account, NULL), -1);
+	protocol_name = g_ascii_strup(purple_protocol_list_icon(protocol, account, NULL), -1);
 
 	buddy_name = purple_normalize(account, sn);
 
diff --git a/libpurple/protocol.h b/libpurple/protocol.h
--- a/libpurple/protocol.h
+++ b/libpurple/protocol.h
@@ -27,6 +27,9 @@
 #ifndef PURPLE_PROTOCOL_H
 #define PURPLE_PROTOCOL_H
 
+#include <glib.h>
+#include <glib-object.h>
+
 /**
  * SECTION:protocol
  * @section_id: libpurple-protocol
@@ -36,15 +39,9 @@
  * #PurpleProtocol is the base type for all protocols in libpurple.
  */
 
-#define PURPLE_TYPE_PROTOCOL            (purple_protocol_get_type())
-#define PURPLE_PROTOCOL(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_PROTOCOL, PurpleProtocol))
-#define PURPLE_PROTOCOL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_PROTOCOL, PurpleProtocolClass))
-#define PURPLE_IS_PROTOCOL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL))
-#define PURPLE_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_PROTOCOL))
-#define PURPLE_PROTOCOL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_PROTOCOL, PurpleProtocolClass))
-
-typedef struct _PurpleProtocol PurpleProtocol;
-typedef struct _PurpleProtocolClass PurpleProtocolClass;
+#define PURPLE_TYPE_PROTOCOL (purple_protocol_get_type())
+G_DECLARE_DERIVABLE_TYPE(PurpleProtocol, purple_protocol, PURPLE, PROTOCOL,
+                         GObject)
 
 #include "account.h"
 #include "buddyicon.h"
@@ -67,39 +64,9 @@
 
 /**
  * PurpleProtocol:
- * @id:              Protocol ID
- * @name:            Translated name of the protocol
- * @options:         Protocol options
- * @user_splits:     A GList of PurpleAccountUserSplit
- * @account_options: A GList of PurpleAccountOption
- * @icon_spec:       The icon spec.
- * @whiteboard_ops:  Whiteboard operations
  *
- * Represents an instance of a protocol registered with the protocols
- * subsystem. Protocols must initialize the members to appropriate values.
+ * Represents an instance of a protocol registered with #PurpleProtocolManager.
  */
-struct _PurpleProtocol
-{
-	GObject gparent;
-
-	/*< public >*/
-	const char *id;
-	const char *name;
-
-	PurpleProtocolOptions options;
-
-	GList *user_splits;
-	GList *account_options;
-
-	PurpleBuddyIconSpec *icon_spec;
-	PurpleWhiteboardOps *whiteboard_ops;
-
-	/*< private >*/
-	void (*_purple_reserved1)(void);
-	void (*_purple_reserved2)(void);
-	void (*_purple_reserved3)(void);
-	void (*_purple_reserved4)(void);
-};
 
 /**
  * PurpleProtocolClass:
@@ -119,23 +86,24 @@
 /* If adding new methods to this class, ensure you add checks for them in
    purple_protocols_add().
 */
-struct _PurpleProtocolClass
-{
+struct _PurpleProtocolClass {
 	GObjectClass parent_class;
 
+	GList *(*get_user_splits)(PurpleProtocol *protocol);
+	GList *(*get_account_options)(PurpleProtocol *protocol);
+	PurpleBuddyIconSpec *(*get_buddy_icon_spec)(PurpleProtocol *protocol);
+	PurpleWhiteboardOps *(*get_whiteboard_ops)(PurpleProtocol *protocol);
+
 	void (*login)(PurpleAccount *account);
 
 	void (*close)(PurpleConnection *connection);
 
 	GList *(*status_types)(PurpleAccount *account);
 
-	const char *(*list_icon)(PurpleAccount *account, PurpleBuddy *buddy);
+	const gchar *(*list_icon)(PurpleAccount *account, PurpleBuddy *buddy);
 
 	/*< private >*/
-	void (*_purple_reserved1)(void);
-	void (*_purple_reserved2)(void);
-	void (*_purple_reserved3)(void);
-	void (*_purple_reserved4)(void);
+	gpointer reserved[4];
 };
 
 /**
@@ -153,102 +121,142 @@
 
 G_BEGIN_DECLS
 
-/**************************************************************************/
-/* Protocol Object API                                                    */
-/**************************************************************************/
-
-/**
- * purple_protocol_get_type:
- *
- * Returns: The #GType for #PurpleProtocol.
- */
-GType purple_protocol_get_type(void);
-
 /**
  * purple_protocol_get_id:
- * @protocol: The protocol.
+ * @protocol: The #PurpleProtocol instance.
  *
- * Returns the ID of a protocol.
+ * Gets the ID of a protocol.
  *
  * Returns: The ID of the protocol.
+ *
+ * Since: 3.0.0
  */
-const char *purple_protocol_get_id(const PurpleProtocol *protocol);
+const gchar *purple_protocol_get_id(PurpleProtocol *protocol);
 
 /**
  * purple_protocol_get_name:
- * @protocol: The protocol.
+ * @protocol: The #PurpleProtocol instance.
  *
- * Returns the translated name of a protocol.
+ * Gets the translated name of a protocol.
  *
  * Returns: The translated name of the protocol.
+ *
+ * Since: 3.0.0
  */
-const char *purple_protocol_get_name(const PurpleProtocol *protocol);
+const gchar *purple_protocol_get_name(PurpleProtocol *protocol);
 
 /**
  * purple_protocol_get_options:
- * @protocol: The protocol.
+ * @protocol: The #PurpleProtocol instance.
  *
- * Returns the options of a protocol.
+ * Gets the options of a protocol.
  *
  * Returns: The options of the protocol.
+ *
+ * Since: 3.0.0
  */
-PurpleProtocolOptions purple_protocol_get_options(const PurpleProtocol *protocol);
+PurpleProtocolOptions purple_protocol_get_options(PurpleProtocol *protocol);
 
 /**
  * purple_protocol_get_user_splits:
- * @protocol: The protocol.
+ * @protocol: The #PurpleProtocol instance.
  *
- * Returns the user splits of a protocol.
+ * Gets the user splits of a protocol.
  *
- * Returns: (element-type PurpleAccountUserSplit) (transfer none): The user
+ * Returns: (element-type PurpleAccountUserSplit) (transfer full): The user
  *          splits of the protocol.
+ *
+ * Since: 3.0.0
  */
-GList *purple_protocol_get_user_splits(const PurpleProtocol *protocol);
+GList *purple_protocol_get_user_splits(PurpleProtocol *protocol);
 
 /**
  * purple_protocol_get_account_options:
- * @protocol: The protocol.
+ * @protocol: The #PurpleProtocol instance.
  *
- * Returns the account options for a protocol.
+ * Gets the account options for a protocol.
  *
- * Returns: (element-type PurpleAccountOption) (transfer none): The account
+ * Returns: (element-type PurpleAccountOption) (transfer full): The account
  *          options for the protocol.
+ *
+ * Since: 3.0.0
  */
-GList *purple_protocol_get_account_options(const PurpleProtocol *protocol);
+GList *purple_protocol_get_account_options(PurpleProtocol *protocol);
 
 /**
  * purple_protocol_get_icon_spec:
- * @protocol: The protocol.
+ * @protocol: The #PurpleProtocol instance.
  *
- * Returns the icon spec of a protocol.
+ * Gets the icon spec of a protocol.
  *
- * Returns: The icon spec of the protocol.
+ * Returns: (transfer full): The icon spec of the protocol.
+ *
+ * Since: 3.0.0
  */
-PurpleBuddyIconSpec *purple_protocol_get_icon_spec(const PurpleProtocol *protocol);
+PurpleBuddyIconSpec *purple_protocol_get_icon_spec(PurpleProtocol *protocol);
 
 /**
  * purple_protocol_get_whiteboard_ops:
- * @protocol: The protocol.
+ * @protocol: The #PurpleProtocol instance.
  *
- * Returns the whiteboard ops of a protocol.
+ * Gets the whiteboard ops of a protocol.
  *
  * Returns: (transfer none): The whiteboard ops of the protocol.
+ *
+ * Since: 3.0.0
  */
-PurpleWhiteboardOps *purple_protocol_get_whiteboard_ops(const PurpleProtocol *protocol);
+PurpleWhiteboardOps *purple_protocol_get_whiteboard_ops(PurpleProtocol *protocol);
 
-/**************************************************************************/
-/* Protocol Class API                                                     */
-/**************************************************************************/
+/**
+ * purple_protocol_login:
+ * @protocol: The #PurpleProtocol instance.
+ * @account: The #PurpleAccount to login.
+ *
+ * Logs @account in using @protocol.
+ *
+ * Since: 3.0.0
+ */
+void purple_protocol_login(PurpleProtocol *protocol, PurpleAccount *account);
+
+/**
+ * purple_protocol_close:
+ * @protocol: The #PurpleProtocol instance.
+ * @connection: The #PurpleConnection to close.
+ *
+ * Closes @connection using @protocol.
+ *
+ * Since: 3.0.0
+ */
+void purple_protocol_close(PurpleProtocol *protocol, PurpleConnection *connection);
 
-void purple_protocol_class_login(PurpleProtocol *protocol, PurpleAccount *account);
-
-void purple_protocol_class_close(PurpleProtocol *protocol, PurpleConnection *connection);
+/**
+ * purple_protocol_status_types:
+ * @protocol: The #PurpleProtocol instance.
+ * @account: The #PurpleAccount instance.
+ *
+ * Gets all of the #PurpleStatusType's for @account which uses @protocol.
+ *
+ * Returns: (transfer full) (element-type PurpleStatusType): A list of the
+ *          available PurpleStatusType's for @account with @protocol.
+ *
+ * Since: 3.0.0
+ */
+GList *purple_protocol_status_types(PurpleProtocol *protocol, PurpleAccount *account);
 
-GList *purple_protocol_class_status_types(PurpleProtocol *protocol,
-		PurpleAccount *account);
-
-const char *purple_protocol_class_list_icon(PurpleProtocol *protocol,
-		PurpleAccount *account, PurpleBuddy *buddy);
+/**
+ * purple_protocol_list_icon:
+ * @protocol: The #PurpleProtocol instance.
+ * @account: The #PurpleAccount instance.
+ * @buddy: The #PurpleBuddy instance.
+ *
+ * Gets the icon to show in the contact list for @buddy on @account which uses
+ * @protocol.
+ *
+ * Returns: The name of the icon file to use in the contact list for @buddy.
+ *
+ * Since: 3.0.0
+ */
+const gchar *purple_protocol_list_icon(PurpleProtocol *protocol, PurpleAccount *account, PurpleBuddy *buddy);
 
 G_END_DECLS
 
diff --git a/libpurple/protocol.c b/libpurple/protocol.c
--- a/libpurple/protocol.c
+++ b/libpurple/protocol.c
@@ -1,5 +1,6 @@
 /*
- * purple
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
  *
  * 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
@@ -16,109 +17,152 @@
  * 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
- *
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
+
+#define PURPLE_IN_PROTOCOL_C 1
+
 #include "protocol.h"
 
-static GObjectClass *parent_class;
+#include "enums.h"
 
-/**************************************************************************
- * Protocol Object API
- **************************************************************************/
-const char *
-purple_protocol_get_id(const PurpleProtocol *protocol)
-{
-	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
+enum {
+	PROP_0,
+	PROP_ID,
+	PROP_NAME,
+	PROP_OPTIONS,
+	N_PROPERTIES,
+};
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+typedef struct {
+	gchar *id;
+	gchar *name;
 
-	return protocol->id;
-}
+	PurpleProtocolOptions options;
+} PurpleProtocolPrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(PurpleProtocol, purple_protocol,
+                                    G_TYPE_OBJECT)
 
-const char *
-purple_protocol_get_name(const PurpleProtocol *protocol)
-{
-	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+purple_protocol_set_id(PurpleProtocol *protocol, const gchar *id) {
+	PurpleProtocolPrivate *priv = NULL;
 
-	return protocol->name;
+	priv = purple_protocol_get_instance_private(protocol);
+	g_free(priv->id);
+	priv->id = g_strdup(id);
+
+	g_object_notify_by_pspec(G_OBJECT(protocol), properties[PROP_ID]);
 }
 
-PurpleProtocolOptions
-purple_protocol_get_options(const PurpleProtocol *protocol)
-{
-	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), 0);
-
-	return protocol->options;
-}
-
-GList *
-purple_protocol_get_user_splits(const PurpleProtocol *protocol)
-{
-	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
-
-	return protocol->user_splits;
-}
+static void
+purple_protocol_set_name(PurpleProtocol *protocol, const gchar *name) {
+	PurpleProtocolPrivate *priv = NULL;
 
-GList *
-purple_protocol_get_account_options(const PurpleProtocol *protocol)
-{
-	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
-
-	return protocol->account_options;
-}
+	priv = purple_protocol_get_instance_private(protocol);
+	g_free(priv->name);
+	priv->name = g_strdup(name);
 
-PurpleBuddyIconSpec *
-purple_protocol_get_icon_spec(const PurpleProtocol *protocol)
-{
-	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
-
-	return protocol->icon_spec;
-}
-
-PurpleWhiteboardOps *
-purple_protocol_get_whiteboard_ops(const PurpleProtocol *protocol)
-{
-	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
-
-	return protocol->whiteboard_ops;
+	g_object_notify_by_pspec(G_OBJECT(protocol), properties[PROP_NAME]);
 }
 
 static void
-icon_spec_free(PurpleProtocol *protocol)
+purple_protocol_set_options(PurpleProtocol *protocol,
+                            PurpleProtocolOptions options)
 {
-	g_return_if_fail(PURPLE_IS_PROTOCOL(protocol));
+	PurpleProtocolPrivate *priv = NULL;
 
-	g_free(protocol->icon_spec);
-	protocol->icon_spec = NULL;
+	priv = purple_protocol_get_instance_private(protocol);
+	priv->options = options;
+
+	g_object_notify_by_pspec(G_OBJECT(protocol), properties[PROP_OPTIONS]);
 }
 
-/**************************************************************************
- * GObject stuff
- **************************************************************************/
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
 static void
-purple_protocol_init(GTypeInstance *instance, gpointer klass)
+purple_protocol_get_property(GObject *obj, guint param_id, GValue *value,
+                             GParamSpec *pspec)
 {
+	PurpleProtocol *protocol = PURPLE_PROTOCOL(obj);
+
+	switch(param_id) {
+		case PROP_ID:
+			g_value_set_string(value, purple_protocol_get_id(protocol));
+			break;
+		case PROP_NAME:
+			g_value_set_string(value, purple_protocol_get_name(protocol));
+			break;
+		case PROP_OPTIONS:
+			g_value_set_flags(value, purple_protocol_get_options(protocol));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
 }
 
 static void
-purple_protocol_finalize(GObject *object)
+purple_protocol_set_property(GObject *obj, guint param_id, const GValue *value,
+                             GParamSpec *pspec)
 {
+	PurpleProtocol *protocol = PURPLE_PROTOCOL(obj);
+
+	switch(param_id) {
+		case PROP_ID:
+			purple_protocol_set_id(protocol, g_value_get_string(value));
+			break;
+		case PROP_NAME:
+			purple_protocol_set_name(protocol, g_value_get_string(value));
+			break;
+		case PROP_OPTIONS:
+			purple_protocol_set_options(protocol, g_value_get_flags(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_protocol_init(PurpleProtocol *protocol) {
+}
+
+static void
+purple_protocol_finalize(GObject *object) {
 	PurpleProtocol *protocol = PURPLE_PROTOCOL(object);
+	PurpleProtocolPrivate *priv = NULL;
 	GList *accounts, *l;
 
+	priv = purple_protocol_get_instance_private(protocol);
+
+	g_clear_pointer(&priv->id, g_free);
+	g_clear_pointer(&priv->name, g_free);
+
+	/* I'm not sure that we can finalize a protocol plugin if an account is
+	 * still using it..  Right now accounts don't ref protocols, but maybe
+	 * they should?
+	 */
 	accounts = purple_accounts_get_all_active();
 	for (l = accounts; l != NULL; l = l->next) {
 		PurpleAccount *account = PURPLE_ACCOUNT(l->data);
 		if (purple_account_is_disconnected(account))
 			continue;
 
-		if (purple_strequal(protocol->id,
-				purple_account_get_protocol_id(account)))
+		if (purple_strequal(priv->id, purple_account_get_protocol_id(account)))
 			purple_account_disconnect(account);
 	}
 
 	g_list_free(accounts);
 
+	/* these seem to be fallbacks if the subclass protocol doesn't do it's own
+	 * clean up?  I kind of want to delete them... - gk 2021-03-03
+	 */
 	purple_request_close_with_handle(protocol);
 	purple_notify_close_with_handle(protocol);
 
@@ -127,85 +171,200 @@
 
 	purple_prefs_disconnect_by_handle(protocol);
 
-	g_list_free_full(protocol->user_splits, (GDestroyNotify)purple_account_user_split_destroy);
-	g_list_free_full(protocol->account_options, (GDestroyNotify)purple_account_option_destroy);
-	icon_spec_free(protocol);
-
-	parent_class->finalize(object);
+	G_OBJECT_CLASS(purple_protocol_parent_class)->finalize(object);
 }
 
 static void
-purple_protocol_class_init(PurpleProtocolClass *klass)
-{
+purple_protocol_class_init(PurpleProtocolClass *klass) {
 	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
 
-	parent_class = g_type_class_peek_parent(klass);
+	obj_class->get_property = purple_protocol_get_property;
+	obj_class->set_property = purple_protocol_set_property;
+	obj_class->finalize = purple_protocol_finalize;
+
+	/**
+	 * PurpleProtocol::id:
+	 *
+	 * The identifier for the protocol.
+	 */
+	properties[PROP_ID] = g_param_spec_string(
+		"id", "id",
+		"The identifier for the protocol",
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * PurpleProtocol::name:
+	 *
+	 * The name to show in user interface for the protocol.
+	 */
+	properties[PROP_NAME] = g_param_spec_string(
+		"name", "name",
+		"The name of the protocol to show in the user interface",
+		NULL,
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
 
-	obj_class->finalize = purple_protocol_finalize;
+	/**
+	 * PurpleProtocol::options:
+	 *
+	 * The #PurpleProtocolOptions for the protocol.
+	 */
+	properties[PROP_OPTIONS] = g_param_spec_flags(
+		"options", "options",
+		"The options for the protocol",
+		PURPLE_TYPE_PROTOCOL_OPTIONS,
+		0,
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+const gchar *
+purple_protocol_get_id(PurpleProtocol *protocol) {
+	PurpleProtocolPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
+
+	priv = purple_protocol_get_instance_private(protocol);
+
+	return priv->id;
 }
 
-GType
-purple_protocol_get_type(void)
-{
-	static GType type = 0;
+const gchar *
+purple_protocol_get_name(PurpleProtocol *protocol) {
+	PurpleProtocolPrivate *priv = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
+
+	priv = purple_protocol_get_instance_private(protocol);
+
+	return priv->name;
+}
+
+PurpleProtocolOptions
+purple_protocol_get_options(PurpleProtocol *protocol) {
+	PurpleProtocolPrivate *priv = NULL;
 
-	if (G_UNLIKELY(type == 0)) {
-		static const GTypeInfo info = {
-			.class_size = sizeof(PurpleProtocolClass),
-			.class_init = (GClassInitFunc)purple_protocol_class_init,
-			.instance_size = sizeof(PurpleProtocol),
-			.instance_init = (GInstanceInitFunc)purple_protocol_init,
-		};
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), 0);
+
+	priv = purple_protocol_get_instance_private(protocol);
+
+	return priv->options;
+}
 
-		type = g_type_register_static(G_TYPE_OBJECT, "PurpleProtocol",
-		                              &info, G_TYPE_FLAG_ABSTRACT);
+GList *
+purple_protocol_get_user_splits(PurpleProtocol *protocol) {
+	PurpleProtocolClass *klass = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
+
+	klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
+	if(klass != NULL && klass->get_user_splits != NULL) {
+		return klass->get_user_splits(protocol);
 	}
 
-	return type;
+	return NULL;
 }
 
-/**************************************************************************
- * Protocol Class API
- **************************************************************************/
-#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
-	PurpleProtocolClass *klass = PURPLE_PROTOCOL_GET_CLASS(protocol); \
-	g_return_if_fail(klass != NULL); \
-	if (klass->funcname) \
-		klass->funcname(__VA_ARGS__);
+GList *
+purple_protocol_get_account_options(PurpleProtocol *protocol) {
+	PurpleProtocolClass *klass = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
+
+	klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
+	if(klass != NULL && klass->get_account_options != NULL) {
+		return klass->get_account_options(protocol);
+	}
+
+	return NULL;
+}
+
+PurpleBuddyIconSpec *
+purple_protocol_get_icon_spec(PurpleProtocol *protocol) {
+	PurpleProtocolClass *klass = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
 
-#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
-	PurpleProtocolClass *klass = PURPLE_PROTOCOL_GET_CLASS(protocol); \
-	g_return_val_if_fail(klass != NULL, defaultreturn); \
-	if (klass->funcname) \
-		return klass->funcname(__VA_ARGS__); \
-	else \
-		return defaultreturn;
+	klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
+	if(klass != NULL && klass->get_buddy_icon_spec != NULL) {
+		return klass->get_buddy_icon_spec(protocol);
+	}
+
+	return NULL;
+}
 
-void
-purple_protocol_class_login(PurpleProtocol *protocol, PurpleAccount *account)
-{
-	DEFINE_PROTOCOL_FUNC(protocol, login, account);
+PurpleWhiteboardOps *
+purple_protocol_get_whiteboard_ops(PurpleProtocol *protocol) {
+	PurpleProtocolClass *klass = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
+
+	klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
+	if(klass != NULL && klass->get_whiteboard_ops != NULL) {
+		return klass->get_whiteboard_ops(protocol);
+	}
+
+	return NULL;
 }
 
 void
-purple_protocol_class_close(PurpleProtocol *protocol, PurpleConnection *gc)
-{
-	DEFINE_PROTOCOL_FUNC(protocol, close, gc);
+purple_protocol_login(PurpleProtocol *protocol, PurpleAccount *account) {
+	PurpleProtocolClass *klass = NULL;
+
+	g_return_if_fail(PURPLE_IS_PROTOCOL(protocol));
+	g_return_if_fail(PURPLE_IS_ACCOUNT(account));
+
+	klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
+	if(klass != NULL && klass->login != NULL) {
+		klass->login(account);
+	}
+}
+
+void
+purple_protocol_close(PurpleProtocol *protocol, PurpleConnection *gc) {
+	PurpleProtocolClass *klass = NULL;
+
+	g_return_if_fail(PURPLE_IS_PROTOCOL(protocol));
+	g_return_if_fail(PURPLE_IS_CONNECTION(gc));
+
+	klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
+	if(klass != NULL && klass->close != NULL) {
+		klass->close(gc);
+	}
 }
 
 GList *
-purple_protocol_class_status_types(PurpleProtocol *protocol,
-		PurpleAccount *account)
+purple_protocol_status_types(PurpleProtocol *protocol, PurpleAccount *account)
 {
-	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, status_types, account);
+	PurpleProtocolClass *klass = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
+	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
+
+	klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
+	if(klass != NULL && klass->status_types != NULL) {
+		return klass->status_types(account);
+	}
+
+	return NULL;
 }
 
-const char *
-purple_protocol_class_list_icon(PurpleProtocol *protocol,
-		PurpleAccount *account, PurpleBuddy *buddy)
+const gchar *
+purple_protocol_list_icon(PurpleProtocol *protocol, PurpleAccount *account,
+                          PurpleBuddy *buddy)
 {
-	DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, list_icon, account, buddy);
-}
+	PurpleProtocolClass *klass = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
 
-#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN
-#undef DEFINE_PROTOCOL_FUNC
+	klass = PURPLE_PROTOCOL_GET_CLASS(protocol);
+	if(klass != NULL && klass->list_icon != NULL) {
+		return klass->list_icon(account, buddy);
+	}
+
+	return NULL;
+}
diff --git a/libpurple/protocols/bonjour/bonjour.c b/libpurple/protocols/bonjour/bonjour.c
--- a/libpurple/protocols/bonjour/bonjour.c
+++ b/libpurple/protocols/bonjour/bonjour.c
@@ -202,6 +202,43 @@
 	return BONJOUR_ICON_NAME;
 }
 
+static GList *
+bonjour_protocol_get_account_options(PurpleProtocol *protocol) {
+	PurpleAccountOption *option;
+	GList *opts = NULL;
+
+	/* Creating the options for the protocol */
+	option = purple_account_option_int_new(_("Local Port"), "port",
+	                                       BONJOUR_DEFAULT_PORT);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_string_new(_("First name"), "first",
+	                                          default_firstname);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_string_new(_("Last name"), "last",
+	                                          default_lastname);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_string_new(_("Email"), "email", "");
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_string_new(_("AIM Account"), "AIM", "");
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_string_new(_("XMPP Account"), "jid", "");
+	opts = g_list_append(opts, option);
+
+	return opts;
+}
+
+static PurpleBuddyIconSpec *
+bonjour_protocol_get_buddy_icon_spec(PurpleProtocol *protocol) {
+	return purple_buddy_icon_spec_new("png,gif,jpeg",
+	                                  0, 0, 96, 96, 65535,
+	                                  PURPLE_ICON_SCALE_DISPLAY);
+}
+
 static int
 bonjour_send_im(PurpleProtocolIM *im, PurpleConnection *connection, PurpleMessage *msg)
 {
@@ -639,34 +676,6 @@
 static void
 bonjour_protocol_init(BonjourProtocol *self)
 {
-	PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
-	PurpleAccountOption *option;
-
-	protocol->id        = "prpl-bonjour";
-	protocol->name      = "Bonjour";
-	protocol->options   = OPT_PROTO_NO_PASSWORD;
-	protocol->icon_spec = purple_buddy_icon_spec_new("png,gif,jpeg",
-	                                                 0, 0, 96, 96, 65535,
-	                                                 PURPLE_ICON_SCALE_DISPLAY);
-
-	/* Creating the options for the protocol */
-	option = purple_account_option_int_new(_("Local Port"), "port", BONJOUR_DEFAULT_PORT);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_string_new(_("First name"), "first", default_firstname);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_string_new(_("Last name"), "last", default_lastname);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_string_new(_("Email"), "email", "");
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_string_new(_("AIM Account"), "AIM", "");
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_string_new(_("XMPP Account"), "jid", "");
-	protocol->account_options = g_list_append(protocol->account_options, option);
 }
 
 static void
@@ -678,6 +687,9 @@
 	protocol_class->close = bonjour_close;
 	protocol_class->status_types = bonjour_status_types;
 	protocol_class->list_icon = bonjour_list_icon;
+
+	protocol_class->get_account_options = bonjour_protocol_get_account_options;
+	protocol_class->get_buddy_icon_spec = bonjour_protocol_get_buddy_icon_spec;
 }
 
 static void
@@ -734,6 +746,16 @@
         G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
                                       bonjour_protocol_xfer_iface_init));
 
+static PurpleProtocol *
+bonjour_protocol_new(void) {
+	return PURPLE_PROTOCOL(g_object_new(
+		BONJOUR_TYPE_PROTOCOL,
+		"id", "prpl-bonjour",
+		"name", "Bonjour",
+		"options", OPT_PROTO_NO_PASSWORD,
+		NULL));
+}
+
 static PurplePluginInfo *
 plugin_query(GError **error)
 {
@@ -761,7 +783,7 @@
 
 	xep_xfer_register(G_TYPE_MODULE(plugin));
 
-	my_protocol = g_object_new(BONJOUR_TYPE_PROTOCOL, NULL);
+	my_protocol = bonjour_protocol_new();
 	if(!purple_protocol_manager_register(manager, my_protocol, error)) {
 		g_clear_object(&my_protocol);
 
diff --git a/libpurple/protocols/facebook/facebook.c b/libpurple/protocols/facebook/facebook.c
--- a/libpurple/protocols/facebook/facebook.c
+++ b/libpurple/protocols/facebook/facebook.c
@@ -958,6 +958,39 @@
 	g_slist_free(select);
 }
 
+static GList *
+fb_get_account_options(PurpleProtocol *protocol) {
+	GList *opts = NULL;
+	PurpleAccountOption *opt;
+
+	opt = purple_account_option_int_new(_("Buddy list sync interval"),
+	                                    "sync-interval", 5);
+	opts = g_list_prepend(opts, opt);
+
+	opt = purple_account_option_bool_new(_("Mark messages as read on focus"),
+	                                     "mark-read", TRUE);
+	opts = g_list_prepend(opts, opt);
+
+	opt = purple_account_option_bool_new(_("Mark messages as read only when available"),
+	                                     "mark-read-available", FALSE);
+	opts = g_list_prepend(opts, opt);
+
+	opt = purple_account_option_bool_new(_("Show self messages"),
+	                                     "show-self", TRUE);
+	opts = g_list_prepend(opts, opt);
+
+	opt = purple_account_option_bool_new(_("Show unread messages"),
+	                                     "show-unread", TRUE);
+	opts = g_list_prepend(opts, opt);
+
+	opt = purple_account_option_bool_new(_("Open new group chats with "
+	                                       "incoming messages"),
+	                                     "group-chat-open", TRUE);
+	opts = g_list_prepend(opts, opt);
+
+	return g_list_reverse(opts);
+}
+
 static void
 fb_login(PurpleAccount *acct)
 {
@@ -1524,41 +1557,7 @@
 }
 
 static void
-facebook_protocol_init(FacebookProtocol *self)
-{
-	PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
-	GList *opts = NULL;
-	PurpleAccountOption *opt;
-
-	protocol->id      = FB_PROTOCOL_ID;
-	protocol->name    = "Facebook";
-	protocol->options = OPT_PROTO_CHAT_TOPIC;
-
-	opt = purple_account_option_int_new(_("Buddy list sync interval"),
-	                                    "sync-interval", 5);
-	opts = g_list_prepend(opts, opt);
-
-	opt = purple_account_option_bool_new(_("Mark messages as read on focus"),
-	                                     "mark-read", TRUE);
-	opts = g_list_prepend(opts, opt);
-
-	opt = purple_account_option_bool_new(_("Mark messages as read only when available"),
-	                                     "mark-read-available", FALSE);
-	opts = g_list_prepend(opts, opt);
-
-	opt = purple_account_option_bool_new(_("Show self messages"),
-	                                     "show-self", TRUE);
-	opts = g_list_prepend(opts, opt);
-
-	opt = purple_account_option_bool_new(_("Show unread messages"),
-	                                     "show-unread", TRUE);
-	opts = g_list_prepend(opts, opt);
-
-	opt = purple_account_option_bool_new(_("Open new group chats with "
-	                                       "incoming messages"),
-	                                     "group-chat-open", TRUE);
-	opts = g_list_prepend(opts, opt);
-	protocol->account_options = g_list_reverse(opts);
+facebook_protocol_init(FacebookProtocol *self) {
 }
 
 static void
@@ -1566,6 +1565,8 @@
 {
 	PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
 
+	protocol_class->get_account_options  = fb_get_account_options;
+
 	protocol_class->login = fb_login;
 	protocol_class->close = fb_close;
 	protocol_class->status_types = fb_status_types;
@@ -1643,13 +1644,13 @@
 	g_return_if_fail(fb_cmds == NULL);
 
 	id = purple_cmd_register("kick", "s", PURPLE_CMD_P_PROTOCOL, cflags,
-				 fb_protocol->id, fb_cmd_kick,
+				 FB_PROTOCOL_ID, fb_cmd_kick,
 				 _("kick: Kick someone from the chat"),
 				 NULL);
 	fb_cmds = g_slist_prepend(fb_cmds, GUINT_TO_POINTER(id));
 
 	id = purple_cmd_register("leave", "", PURPLE_CMD_P_PROTOCOL, cflags,
-				 fb_protocol->id, fb_cmd_leave,
+				 FB_PROTOCOL_ID, fb_cmd_leave,
 				 _("leave: Leave the chat"),
 				 NULL);
 	fb_cmds = g_slist_prepend(fb_cmds, GUINT_TO_POINTER(id));
@@ -1692,7 +1693,11 @@
 	PurpleProtocolManager *manager = purple_protocol_manager_get_default();
 
 	facebook_protocol_register_type(G_TYPE_MODULE(plugin));
-	fb_protocol = g_object_new(FACEBOOK_TYPE_PROTOCOL, NULL);
+	fb_protocol = g_object_new(FACEBOOK_TYPE_PROTOCOL,
+	                           "id", FB_PROTOCOL_ID,
+	                           "name", "Facebook",
+	                           "options", OPT_PROTO_CHAT_TOPIC,
+	                           NULL);
 
 	if(!purple_protocol_manager_register(manager, fb_protocol, error)) {
 		g_clear_object(&fb_protocol);
diff --git a/libpurple/protocols/gg/gg.c b/libpurple/protocols/gg/gg.c
--- a/libpurple/protocols/gg/gg.c
+++ b/libpurple/protocols/gg/gg.c
@@ -49,7 +49,6 @@
 
 /* ---------------------------------------------------------------------- */
 static PurpleProtocol *my_protocol = NULL;
-static PurpleAccountOption *ggp_server_option;
 
 /* ---------------------------------------------------------------------- */
 
@@ -646,6 +645,62 @@
 	return "gadu-gadu";
 }
 
+static PurpleBuddyIconSpec *
+ggp_protocol_get_buddy_icon_spec(PurpleProtocol *protocol) {
+	return purple_buddy_icon_spec_new("png",
+	                                  1, 1, 200, 200, 0,
+	                                  PURPLE_ICON_SCALE_DISPLAY |
+	                                  PURPLE_ICON_SCALE_SEND);
+}
+
+static GList *
+ggp_protocol_get_account_options(PurpleProtocol *protocol) {
+	PurpleAccountOption *option = NULL;
+	PurpleKeyValuePair *kvp = NULL;
+	GList *encryption_options = NULL;
+	GList *protocol_version = NULL;
+	GList *opts = NULL;
+
+	option = purple_account_option_string_new(_("GG server"), "gg_server", "");
+	opts = g_list_append(opts, option);
+
+	/* setup encryption options */
+	kvp = purple_key_value_pair_new(_("Use encryption if available"),
+	                                "opportunistic_tls");
+	encryption_options = g_list_append(encryption_options, kvp);
+
+	kvp = purple_key_value_pair_new(_("Require encryption"), "require_tls");
+	encryption_options = g_list_append(encryption_options, kvp);
+
+	kvp = purple_key_value_pair_new(_("Don't use encryption"), "none");
+	encryption_options = g_list_append(encryption_options, kvp);
+
+	option = purple_account_option_list_new(_("Connection security"),
+	                                        "encryption", encryption_options);
+	opts = g_list_append(opts, option);
+
+	/* setup the protocol version */
+	kvp = purple_key_value_pair_new(_("Default"), "default");
+	protocol_version = g_list_append(protocol_version, kvp);
+
+	kvp = purple_key_value_pair_new("GG 10", "gg10");
+	protocol_version = g_list_append(protocol_version, kvp);
+
+	kvp = purple_key_value_pair_new("GG 11", "gg11");
+	protocol_version = g_list_append(protocol_version, kvp);
+
+	option = purple_account_option_list_new(_("Protocol version"),
+	                                        "protocol_version",
+	                                        protocol_version);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_bool_new(_("Show links from strangers"),
+	                                        "show_links_from_strangers", 1);
+	opts = g_list_append(opts, option);
+
+	return opts;
+}
+
 static const char *
 ggp_normalize(PurpleProtocolClient *client, PurpleAccount *account,
               const char *who)
@@ -1026,52 +1081,6 @@
 static void
 ggp_protocol_init(GGPProtocol *self)
 {
-	PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
-	PurpleAccountOption *option;
-	GList *encryption_options = NULL;
-	GList *protocol_version = NULL;
-
-	protocol->id        = "prpl-gg";
-	protocol->name      = "Gadu-Gadu";
-	protocol->icon_spec = purple_buddy_icon_spec_new("png",
-	                                                 1, 1, 200, 200, 0,
-	                                                 PURPLE_ICON_SCALE_DISPLAY |
-	                                                 PURPLE_ICON_SCALE_SEND);
-
-	option = purple_account_option_string_new(_("GG server"),
-			"gg_server", "");
-	protocol->account_options = g_list_append(protocol->account_options,
-			option);
-	ggp_server_option = option;
-
-#define ADD_VALUE(list, desc, v) { \
-	PurpleKeyValuePair *kvp = purple_key_value_pair_new_full((desc), g_strdup((v)), g_free); \
-	list = g_list_append(list, kvp); \
-}
-
-	ADD_VALUE(encryption_options, _("Use encryption if available"),
-		"opportunistic_tls");
-	ADD_VALUE(encryption_options, _("Require encryption"), "require_tls");
-	ADD_VALUE(encryption_options, _("Don't use encryption"), "none");
-
-	option = purple_account_option_list_new(_("Connection security"),
-		"encryption", encryption_options);
-	protocol->account_options = g_list_append(protocol->account_options,
-		option);
-
-	ADD_VALUE(protocol_version, _("Default"), "default");
-	ADD_VALUE(protocol_version, "GG 10", "gg10");
-	ADD_VALUE(protocol_version, "GG 11", "gg11");
-
-	option = purple_account_option_list_new(_("Protocol version"),
-		"protocol_version", protocol_version);
-	protocol->account_options = g_list_append(protocol->account_options,
-		option);
-
-	option = purple_account_option_bool_new(_("Show links from strangers"),
-		"show_links_from_strangers", 1);
-	protocol->account_options = g_list_append(protocol->account_options,
-		option);
 }
 
 static void
@@ -1083,6 +1092,9 @@
 	protocol_class->close = ggp_close;
 	protocol_class->status_types = ggp_status_types;
 	protocol_class->list_icon = ggp_list_icon;
+
+	protocol_class->get_account_options = ggp_protocol_get_account_options;
+	protocol_class->get_buddy_icon_spec = ggp_protocol_get_buddy_icon_spec;
 }
 
 static void
@@ -1184,6 +1196,15 @@
         G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
                                       ggp_protocol_xfer_iface_init));
 
+static PurpleProtocol *
+ggp_protocol_new(void) {
+	return PURPLE_PROTOCOL(g_object_new(
+		GGP_TYPE_PROTOCOL,
+		"id", "prpl-gg",
+		"name", "Gadu-Gadu",
+		NULL));
+}
+
 static gchar *
 plugin_extra(PurplePlugin *plugin)
 {
@@ -1224,7 +1245,7 @@
 
 	ggp_xfer_register(G_TYPE_MODULE(plugin));
 
-	my_protocol = g_object_new(GGP_TYPE_PROTOCOL, NULL);
+	my_protocol = ggp_protocol_new();
 	if(!purple_protocol_manager_register(manager, my_protocol, error)) {
 		g_clear_object(&my_protocol);
 
@@ -1238,7 +1259,7 @@
 
 	ggp_libgaduw_setup();
 	ggp_resolver_purple_setup();
-	ggp_servconn_setup(ggp_server_option);
+	//ggp_servconn_setup(NULL);
 	ggp_html_setup();
 	ggp_message_setup_global();
 
diff --git a/libpurple/protocols/gg/servconn.c b/libpurple/protocols/gg/servconn.c
--- a/libpurple/protocols/gg/servconn.c
+++ b/libpurple/protocols/gg/servconn.c
@@ -57,8 +57,10 @@
 		g_list_free_full(extra, g_free);
 	}
 
-	purple_account_option_string_set_hints(global_data.server_option,
-		ggp_servconn_get_servers());
+	if(server_option != NULL) {
+		purple_account_option_string_set_hints(global_data.server_option,
+			ggp_servconn_get_servers());
+	}
 }
 
 void ggp_servconn_cleanup(void)
diff --git a/libpurple/protocols/irc/irc.c b/libpurple/protocols/irc/irc.c
--- a/libpurple/protocols/irc/irc.c
+++ b/libpurple/protocols/irc/irc.c
@@ -357,6 +357,63 @@
 	return "irc";
 }
 
+static GList *
+irc_protocol_get_account_options(PurpleProtocol *protocol) {
+	PurpleAccountOption *option;
+	GList *opts = NULL;
+
+	option = purple_account_option_int_new(_("Port"), "port",
+	                                       IRC_DEFAULT_PORT);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_string_new(_("Encodings"), "encoding",
+	                                          IRC_DEFAULT_CHARSET);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_bool_new(_("Auto-detect incoming UTF-8"),
+	                                        "autodetect_utf8",
+	                                        IRC_DEFAULT_AUTODETECT);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_string_new(_("Ident name"), "username", "");
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_string_new(_("Real name"), "realname", "");
+	opts = g_list_append(opts, option);
+
+	/*
+	option = purple_account_option_string_new(_("Quit message"), "quitmsg",
+	                                          IRC_DEFAULT_QUIT);
+	opts = g_list_append(opts, option);
+	*/
+
+	option = purple_account_option_bool_new(_("Use SSL"), "ssl", FALSE);
+	opts = g_list_append(opts, option);
+
+#ifdef HAVE_CYRUS_SASL
+	option = purple_account_option_bool_new(_("Authenticate with SASL"),
+	                                        "sasl", FALSE);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_bool_new(_("Allow plaintext SASL auth over "
+	                                          "unencrypted connection"),
+	                                        "auth_plain_in_clear", FALSE);
+	opts = g_list_append(opts, option);
+#endif
+
+	return opts;
+}
+
+static GList *
+irc_protocol_get_user_splits(PurpleProtocol *protocol) {
+	PurpleAccountUserSplit *split;
+
+	split = purple_account_user_split_new(_("Server"), IRC_DEFAULT_SERVER,
+	                                      '@');
+
+	return g_list_append(NULL, split);
+}
+
 static GList *irc_status_types(PurpleAccount *account)
 {
 	PurpleStatusType *type;
@@ -972,50 +1029,6 @@
 static void
 irc_protocol_init(IRCProtocol *self)
 {
-	PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
-	PurpleAccountUserSplit *split;
-	PurpleAccountOption *option;
-
-	protocol->id        = "prpl-irc";
-	protocol->name      = "IRC";
-	protocol->options   = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL |
-	                      OPT_PROTO_SLASH_COMMANDS_NATIVE;
-
-	split = purple_account_user_split_new(_("Server"), IRC_DEFAULT_SERVER, '@');
-	protocol->user_splits = g_list_append(protocol->user_splits, split);
-
-	option = purple_account_option_int_new(_("Port"), "port", IRC_DEFAULT_PORT);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_string_new(_("Encodings"), "encoding", IRC_DEFAULT_CHARSET);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_bool_new(_("Auto-detect incoming UTF-8"), "autodetect_utf8", IRC_DEFAULT_AUTODETECT);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_string_new(_("Ident name"), "username", "");
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_string_new(_("Real name"), "realname", "");
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	/*
-	option = purple_account_option_string_new(_("Quit message"), "quitmsg", IRC_DEFAULT_QUIT);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-	*/
-
-	option = purple_account_option_bool_new(_("Use SSL"), "ssl", FALSE);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-#ifdef HAVE_CYRUS_SASL
-	option = purple_account_option_bool_new(_("Authenticate with SASL"), "sasl", FALSE);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_bool_new(
-						_("Allow plaintext SASL auth over unencrypted connection"),
-						"auth_plain_in_clear", FALSE);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-#endif
 }
 
 static void
@@ -1027,6 +1040,9 @@
 	protocol_class->close = irc_close;
 	protocol_class->status_types = irc_status_types;
 	protocol_class->list_icon = irc_blist_icon;
+
+	protocol_class->get_account_options = irc_protocol_get_account_options;
+	protocol_class->get_user_splits = irc_protocol_get_user_splits;
 }
 
 static void
@@ -1107,6 +1123,17 @@
         G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
                                       irc_protocol_xfer_iface_init));
 
+static PurpleProtocol *
+irc_protocol_new(void) {
+	return PURPLE_PROTOCOL(g_object_new(
+		IRC_TYPE_PROTOCOL,
+		"id", "prpl-irc",
+		"name", "IRC",
+		"options", OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL |
+	               OPT_PROTO_SLASH_COMMANDS_NATIVE,
+		NULL));
+}
+
 static PurplePluginInfo *
 plugin_query(GError **error)
 {
@@ -1134,7 +1161,7 @@
 
 	irc_xfer_register(G_TYPE_MODULE(plugin));
 
-	_irc_protocol = g_object_new(IRC_TYPE_PROTOCOL, NULL);
+	_irc_protocol = irc_protocol_new();
 	if(!purple_protocol_manager_register(manager, _irc_protocol, error)) {
 		g_clear_object(&_irc_protocol);
 
diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c
--- a/libpurple/protocols/jabber/jabber.c
+++ b/libpurple/protocols/jabber/jabber.c
@@ -3792,7 +3792,7 @@
 		acct_id = g_hash_table_lookup(params, "account");
 	}
 
-	acct = find_acct(protocol->id, acct_id);
+	acct = find_acct(XMPP_PROTOCOL_ID, acct_id);
 
 	if (!acct)
 		return FALSE;
@@ -4079,24 +4079,16 @@
 		jabber_do_uninit();
 }
 
+static PurpleBuddyIconSpec *
+jabber_protocol_get_buddy_icon_spec(PurpleProtocol *protocol) {
+	return purple_buddy_icon_spec_new("png",
+	                                  32, 32, 96, 96, 0,
+	                                  PURPLE_ICON_SCALE_SEND |
+	                                  PURPLE_ICON_SCALE_DISPLAY);
+}
+
 static void
-jabber_protocol_init(JabberProtocol *self)
-{
-	PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
-
-	protocol->id        = "prpl-jabber";
-	protocol->name      = "XMPP";
-	protocol->options   = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
-	                      OPT_PROTO_MAIL_CHECK |
-#ifdef HAVE_CYRUS_SASL
-	                      OPT_PROTO_PASSWORD_OPTIONAL |
-#endif
-	                      OPT_PROTO_SLASH_COMMANDS_NATIVE;
-
-	protocol->icon_spec = purple_buddy_icon_spec_new("png",
-	                                                 32, 32, 96, 96, 0,
-	                                                 PURPLE_ICON_SCALE_SEND |
-	                                                 PURPLE_ICON_SCALE_DISPLAY);
+jabber_protocol_init(JabberProtocol *self) {
 }
 
 static void
@@ -4104,6 +4096,8 @@
 {
 	PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
 
+	protocol_class->get_buddy_icon_spec = jabber_protocol_get_buddy_icon_spec;
+
 	protocol_class->login = jabber_login;
 	protocol_class->close = jabber_close;
 	protocol_class->status_types = jabber_status_types;
@@ -4302,7 +4296,7 @@
 
 	jabber_si_xfer_register(G_TYPE_MODULE(plugin));
 
-	xmpp_protocol = g_object_new(XMPP_TYPE_PROTOCOL, NULL);
+	xmpp_protocol = xmpp_protocol_new();
 	if(!purple_protocol_manager_register(manager, xmpp_protocol, error)) {
 		g_clear_object(&xmpp_protocol);
 
diff --git a/libpurple/protocols/jabber/xmpp.h b/libpurple/protocols/jabber/xmpp.h
--- a/libpurple/protocols/jabber/xmpp.h
+++ b/libpurple/protocols/jabber/xmpp.h
@@ -25,6 +25,8 @@
 
 #include "jabber.h"
 
+#define XMPP_PROTOCOL_ID "prpl-jabber"
+
 #define XMPP_TYPE_PROTOCOL             (xmpp_protocol_get_type())
 #define XMPP_PROTOCOL(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), XMPP_TYPE_PROTOCOL, XMPPProtocol))
 #define XMPP_PROTOCOL_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST((klass), XMPP_TYPE_PROTOCOL, XMPPProtocolClass))
@@ -48,6 +50,9 @@
 G_GNUC_INTERNAL
 void xmpp_protocol_register(PurplePlugin *plugin);
 
+G_GNUC_INTERNAL
+PurpleProtocol *xmpp_protocol_new(void);
+
 /**
  * Returns the GType for the XMPPProtocol object.
  */
diff --git a/libpurple/protocols/jabber/xmpp.c b/libpurple/protocols/jabber/xmpp.c
--- a/libpurple/protocols/jabber/xmpp.c
+++ b/libpurple/protocols/jabber/xmpp.c
@@ -20,84 +20,96 @@
  *
  */
 
+#include <config.h>
+
 #include <glib/gi18n-lib.h>
 
 #include <purple.h>
 
 #include "xmpp.h"
 
-static void
-xmpp_protocol_init(XMPPProtocol *self)
-{
-	PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
-	PurpleAccountUserSplit *split;
+static GList *
+xmpp_protocol_get_account_options(PurpleProtocol *protocol) {
 	PurpleAccountOption *option;
-	GList *encryption_values = NULL;
+	PurpleKeyValuePair *kvp = NULL;
+	GList *opts = NULL, *encryption_values = NULL;
 
-	/* Translators: 'domain' is used here in the context of Internet domains, e.g. pidgin.im */
-	split = purple_account_user_split_new(_("Domain"), NULL, '@');
-	purple_account_user_split_set_reverse(split, FALSE);
-	protocol->user_splits = g_list_append(protocol->user_splits, split);
+	/* build the list of encryption types we support */
+	kvp = purple_key_value_pair_new(_("Require encryption"), "require_tls");
+	encryption_values = g_list_append(encryption_values, kvp);
 
-	split = purple_account_user_split_new(_("Resource"), "", '/');
-	purple_account_user_split_set_reverse(split, FALSE);
-	protocol->user_splits = g_list_append(protocol->user_splits, split);
+	kvp = purple_key_value_pair_new(_("Use encryption if available"),
+	                                "opportunistic_tls");
+	encryption_values = g_list_append(encryption_values, kvp);
 
-#define ADD_VALUE(list, desc, v) { \
-	PurpleKeyValuePair *kvp = purple_key_value_pair_new_full((desc), g_strdup((v)), g_free); \
-	list = g_list_prepend(list, kvp); \
-}
-
-	ADD_VALUE(encryption_values, _("Require encryption"), "require_tls");
-	ADD_VALUE(encryption_values, _("Use encryption if available"), "opportunistic_tls");
-	ADD_VALUE(encryption_values, _("Use old-style SSL"), "old_ssl");
-	encryption_values = g_list_reverse(encryption_values);
+	kvp = purple_key_value_pair_new(_("Use old-style SSL"), "old_ssl");
+	encryption_values = g_list_append(encryption_values, kvp);
 
-#undef ADD_VALUE
-
-	option = purple_account_option_list_new(_("Connection security"), "connection_security", encryption_values);
-	protocol->account_options = g_list_append(protocol->account_options,
-						   option);
+	/* build all the options */
+	option = purple_account_option_list_new(_("Connection security"),
+	                                        "connection_security",
+	                                        encryption_values);
+	opts = g_list_append(opts, option);
 
-	option = purple_account_option_bool_new(
-						_("Allow plaintext auth over unencrypted streams"),
-						"auth_plain_in_clear", FALSE);
-	protocol->account_options = g_list_append(protocol->account_options,
-						   option);
+	option = purple_account_option_bool_new(_("Allow plaintext auth over "
+	                                          "unencrypted streams"),
+	                                        "auth_plain_in_clear", FALSE);
+	opts = g_list_append(opts, option);
 
 	option = purple_account_option_int_new(_("Connect port"), "port", 5222);
-	protocol->account_options = g_list_append(protocol->account_options,
-						   option);
+	opts = g_list_append(opts, option);
 
 	option = purple_account_option_string_new(_("Connect server"),
-						  "connect_server", NULL);
-	protocol->account_options = g_list_append(protocol->account_options,
-						  option);
+	                                          "connect_server", NULL);
+	opts = g_list_append(opts, option);
 
 	option = purple_account_option_string_new(_("File transfer proxies"),
-						  "ft_proxies", NULL);
-	protocol->account_options = g_list_append(protocol->account_options,
-						  option);
+	                                          "ft_proxies", NULL);
+	opts = g_list_append(opts, option);
 
-	option = purple_account_option_string_new(_("BOSH URL"),
-						  "bosh_url", NULL);
-	protocol->account_options = g_list_append(protocol->account_options,
-						  option);
+	option = purple_account_option_string_new(_("BOSH URL"), "bosh_url", NULL);
+	opts = g_list_append(opts, option);
 
 	/* this should probably be part of global smiley theme settings
 	 * later on
 	 */
 	option = purple_account_option_bool_new(_("Show Custom Smileys"),
-		"custom_smileys", TRUE);
-	protocol->account_options = g_list_append(protocol->account_options,
-		option);
+	                                        "custom_smileys", TRUE);
+	opts = g_list_append(opts, option);
+
+	return opts;
+}
+
+static GList *
+xmpp_protocol_get_user_splits(PurpleProtocol *protocol) {
+	GList *splits = NULL;
+	PurpleAccountUserSplit *split;
 
+	/* Translators: 'domain' is used here in the context of Internet domains,
+	 * e.g. pidgin.im.
+	 */
+	split = purple_account_user_split_new(_("Domain"), NULL, '@');
+	purple_account_user_split_set_reverse(split, FALSE);
+	splits = g_list_append(splits, split);
+
+	split = purple_account_user_split_new(_("Resource"), "", '/');
+	purple_account_user_split_set_reverse(split, FALSE);
+	splits = g_list_append(splits, split);
+
+	return splits;
+}
+
+static void
+xmpp_protocol_init(XMPPProtocol *self) {
 	purple_prefs_remove("/plugins/prpl/jabber");
 }
 
 static void
-xmpp_protocol_class_init(G_GNUC_UNUSED XMPPProtocolClass *klass)
-{
+xmpp_protocol_class_init(XMPPProtocolClass *klass) {
+	PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
+
+	protocol_class->get_account_options = xmpp_protocol_get_account_options;
+	protocol_class->get_user_splits = xmpp_protocol_get_user_splits;
 }
 
 static void
@@ -114,3 +126,23 @@
 {
 	xmpp_protocol_register_type(G_TYPE_MODULE(plugin));
 }
+
+PurpleProtocol *
+xmpp_protocol_new(void) {
+	PurpleProtocolOptions options;
+
+	options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
+	          OPT_PROTO_MAIL_CHECK | OPT_PROTO_SLASH_COMMANDS_NATIVE;
+
+#ifdef HAVE_CYRUS_SASL
+	options |= OPT_PROTO_PASSWORD_OPTIONAL;
+#endif
+
+	return PURPLE_PROTOCOL(g_object_new(
+		XMPP_TYPE_PROTOCOL,
+		"id", XMPP_PROTOCOL_ID,
+		"name", "XMPP",
+		"options", options,
+		NULL
+	));
+}
\ No newline at end of file
diff --git a/libpurple/protocols/novell/novell.c b/libpurple/protocols/novell/novell.c
--- a/libpurple/protocols/novell/novell.c
+++ b/libpurple/protocols/novell/novell.c
@@ -2831,6 +2831,22 @@
 	return "novell";
 }
 
+static GList *
+novell_protocol_get_account_options(PurpleProtocol *protocol) {
+	PurpleAccountOption *option;
+	GList *opts = NULL;
+
+	option = purple_account_option_string_new(_("Server address"), "server",
+	                                          NULL);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_int_new(_("Server port"), "port",
+	                                       DEFAULT_PORT);
+	opts = g_list_append(opts, option);
+
+	return opts;
+}
+
 static void
 novell_tooltip_text(PurpleProtocolClient *client, PurpleBuddy *buddy,
                     PurpleNotifyUserInfo *user_info, gboolean full)
@@ -3507,19 +3523,6 @@
 static void
 novell_protocol_init(NovellProtocol *self)
 {
-	PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
-	PurpleAccountOption *option;
-
-	protocol->id   = "prpl-novell";
-	protocol->name = "GroupWise";
-
-	option = purple_account_option_string_new(_("Server address"), "server", NULL);
-	protocol->account_options =
-		g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_int_new(_("Server port"), "port", DEFAULT_PORT);
-	protocol->account_options =
-		g_list_append(protocol->account_options, option);
 }
 
 static void
@@ -3531,6 +3534,8 @@
 	protocol_class->close = novell_close;
 	protocol_class->status_types = novell_status_types;
 	protocol_class->list_icon = novell_list_icon;
+
+	protocol_class->get_account_options = novell_protocol_get_account_options;
 }
 
 static void
@@ -3607,6 +3612,15 @@
         G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
                                       novell_protocol_privacy_iface_init));
 
+static PurpleProtocol *
+novell_protocol_new(void) {
+	return PURPLE_PROTOCOL(g_object_new(
+		NOVELL_TYPE_PROTOCOL,
+		"id", "prpl-novell",
+		"name", "GroupWise",
+		NULL));
+}
+
 static PurplePluginInfo *
 plugin_query(GError **error)
 {
@@ -3632,7 +3646,7 @@
 
 	novell_protocol_register_type(G_TYPE_MODULE(plugin));
 
-	my_protocol = g_object_new(NOVELL_TYPE_PROTOCOL, NULL);
+	my_protocol = novell_protocol_new();
 	if(!purple_protocol_manager_register(manager, my_protocol, error)) {
 		g_clear_object(&my_protocol);
 
diff --git a/libpurple/protocols/null/nullprpl.c b/libpurple/protocols/null/nullprpl.c
--- a/libpurple/protocols/null/nullprpl.c
+++ b/libpurple/protocols/null/nullprpl.c
@@ -205,6 +205,43 @@
   return "null";
 }
 
+static GList *
+null_protocol_get_account_options(PurpleProtocol *protocol) {
+  PurpleAccountOption *option;
+
+  option = purple_account_option_string_new(
+          _("Example option"), /* text shown to user */
+          "example",           /* pref name */
+          "default");          /* default value */
+
+  return g_list_append(NULL, option);
+}
+
+static PurpleBuddyIconSpec *
+null_protocol_get_buddy_icon_spec(PurpleProtocol *protocol) {
+  return purple_buddy_icon_spec_new(
+          "png,jpg,gif",            /* format */
+          0,                        /* min_width */
+          0,                        /* min_height */
+          128,                      /* max_width */
+          128,                      /* max_height */
+          10000,                    /* max_filesize */
+          PURPLE_ICON_SCALE_DISPLAY /* scale_rules */
+  );
+}
+
+static GList *
+null_protocol_get_user_splits(PurpleProtocol *protocol) {
+  PurpleAccountUserSplit *split;
+
+  split = purple_account_user_split_new(
+          _("Example user split"), /* text shown to user */
+          "default",               /* default value */
+          '@');                    /* field separator */
+
+  return g_list_append(NULL, split);
+}
+
 static char *
 null_status_text(PurpleProtocolClient *client, PurpleBuddy *buddy) {
   purple_debug_info("nullprpl", "getting %s's status text for %s\n",
@@ -1048,36 +1085,6 @@
 static void
 null_protocol_init(NullProtocol *self)
 {
-	PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
-	PurpleAccountUserSplit *split;
-	PurpleAccountOption *option;
-
-	protocol->id = "prpl-null";
-	protocol->name = "Null - Testing Protocol";
-	protocol->options = OPT_PROTO_NO_PASSWORD | OPT_PROTO_CHAT_TOPIC;
-	protocol->icon_spec = purple_buddy_icon_spec_new(
-	        "png,jpg,gif",            /* format */
-	        0,                        /* min_width */
-	        0,                        /* min_height */
-	        128,                      /* max_width */
-	        128,                      /* max_height */
-	        10000,                    /* max_filesize */
-	        PURPLE_ICON_SCALE_DISPLAY /* scale_rules */
-	);
-
-	/* see accountopt.h for information about user splits and protocol
-	 * options */
-	split = purple_account_user_split_new(
-	        _("Example user split"), /* text shown to user */
-	        "default",               /* default value */
-	        '@');                    /* field separator */
-	option = purple_account_option_string_new(
-	        _("Example option"), /* text shown to user */
-	        "example",           /* pref name */
-	        "default");          /* default value */
-
-	protocol->user_splits = g_list_append(NULL, split);
-	protocol->account_options = g_list_append(NULL, option);
 }
 
 /*
@@ -1094,6 +1101,10 @@
 	protocol_class->close = null_close;
 	protocol_class->status_types = null_status_types;
 	protocol_class->list_icon = null_list_icon;
+
+  protocol_class->get_account_options = null_protocol_get_account_options;
+  protocol_class->get_buddy_icon_spec = null_protocol_get_buddy_icon_spec;
+  protocol_class->get_user_splits = null_protocol_get_user_splits;
 }
 
 static void
@@ -1199,6 +1210,16 @@
         G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST,
                                       null_protocol_roomlist_iface_init));
 
+static PurpleProtocol *
+null_protocol_new(void) {
+  return PURPLE_PROTOCOL(g_object_new(
+    NULL_TYPE_PROTOCOL,
+    "id", "prpl-null",
+    "name", "Null - Testing Protocol",
+    "options", OPT_PROTO_NO_PASSWORD | OPT_PROTO_CHAT_TOPIC,
+    NULL));
+}
+
 static PurplePluginInfo *
 plugin_query(GError **error)
 {
@@ -1230,7 +1251,7 @@
   null_protocol_register_type(G_TYPE_MODULE(plugin));
 
   /* add the protocol to the core */
-  my_protocol = g_object_new(NULL_TYPE_PROTOCOL, NULL);
+  my_protocol = null_protocol_new();
   if(!purple_protocol_manager_register(manager, my_protocol, error)) {
     g_clear_object(&my_protocol);
 
diff --git a/libpurple/protocols/sametime/sametime.c b/libpurple/protocols/sametime/sametime.c
--- a/libpurple/protocols/sametime/sametime.c
+++ b/libpurple/protocols/sametime/sametime.c
@@ -3118,6 +3118,49 @@
   return "meanwhile";
 }
 
+static GList *
+mw_protocol_get_account_options(PurpleProtocol *protocol) {
+  PurpleAccountOption *opt;
+  GList *opts = NULL;
+
+  /* port to connect to */
+  opt = purple_account_option_int_new(_("Port"), MW_KEY_PORT,
+                                      MW_PLUGIN_DEFAULT_PORT);
+  opts = g_list_append(opts, opt);
+
+  { /* copy the old force login setting from prefs if it's
+       there. Don't delete the preference, since there may be more
+       than one account that wants to check for it. */
+    gboolean b = FALSE;
+    const gchar *label = _("Force login (ignore server redirects)");
+
+    if (purple_prefs_exists(MW_PROTOCOL_OPT_FORCE_LOGIN))
+      b = purple_prefs_get_bool(MW_PROTOCOL_OPT_FORCE_LOGIN);
+
+    opt = purple_account_option_bool_new(label, MW_KEY_FORCE, b);
+    opts = g_list_append(opts, opt);
+  }
+
+  /* pretend to be Sametime Connect */
+  opt = purple_account_option_bool_new(_("Hide client identity"),
+                                       MW_KEY_FAKE_IT, FALSE);
+  opts = g_list_append(opts, opt);
+
+  return opts;
+}
+
+static GList *
+mw_protocol_get_user_splits(PurpleProtocol *protocol) {
+  PurpleAccountUserSplit *split;
+  GList *splits = NULL;
+
+  /* set up account ID as user:server */
+  split = purple_account_user_split_new(_("Server"),
+                                        MW_PLUGIN_DEFAULT_HOST, ':');
+  splits = g_list_append(splits, split);
+
+  return splits;
+}
 
 static const char *
 mw_protocol_list_emblem(PurpleProtocolClient *client, PurpleBuddy *b) {
@@ -5209,53 +5252,14 @@
 static void
 mw_protocol_init(mwProtocol *self)
 {
-	PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
-	PurpleAccountUserSplit *split;
-	PurpleAccountOption *opt;
-	GList *l = NULL;
-
-	protocol->id = PROTOCOL_ID;
-	protocol->name = PROTOCOL_NAME;
-
 	/* set up the preferences */
 	purple_prefs_add_none(MW_PROTOCOL_OPT_BASE);
 	purple_prefs_add_int(MW_PROTOCOL_OPT_BLIST_ACTION,
 	                     BLIST_CHOICE_DEFAULT);
 
-	/* set up account ID as user:server */
-	split = purple_account_user_split_new(_("Server"),
-	                                      MW_PLUGIN_DEFAULT_HOST, ':');
-	protocol->user_splits = g_list_append(protocol->user_splits, split);
-
 	/* remove dead preferences */
 	purple_prefs_remove(MW_PROTOCOL_OPT_PSYCHIC);
 	purple_prefs_remove(MW_PROTOCOL_OPT_SAVE_DYNAMIC);
-
-	/* port to connect to */
-	opt = purple_account_option_int_new(_("Port"), MW_KEY_PORT,
-	                                    MW_PLUGIN_DEFAULT_PORT);
-	l = g_list_append(l, opt);
-
-	{ /* copy the old force login setting from prefs if it's
-	     there. Don't delete the preference, since there may be more
-	     than one account that wants to check for it. */
-		gboolean b = FALSE;
-		const char *label = _("Force login (ignore server redirects)");
-
-		if (purple_prefs_exists(MW_PROTOCOL_OPT_FORCE_LOGIN))
-			b = purple_prefs_get_bool(MW_PROTOCOL_OPT_FORCE_LOGIN);
-
-		opt = purple_account_option_bool_new(label, MW_KEY_FORCE, b);
-		l = g_list_append(l, opt);
-	}
-
-	/* pretend to be Sametime Connect */
-	opt = purple_account_option_bool_new(_("Hide client identity"),
-	                                     MW_KEY_FAKE_IT, FALSE);
-	l = g_list_append(l, opt);
-
-	protocol->account_options = l;
-	l = NULL;
 }
 
 static void
@@ -5267,6 +5271,9 @@
 	protocol_class->close = mw_protocol_close;
 	protocol_class->status_types = mw_protocol_status_types;
 	protocol_class->list_icon = mw_protocol_list_icon;
+
+  protocol_class->get_account_options = mw_protocol_get_account_options;
+  protocol_class->get_user_splits = mw_protocol_get_user_splits;
 }
 
 static void
@@ -5368,6 +5375,15 @@
         G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
                                       mw_protocol_xfer_iface_init));
 
+static PurpleProtocol *
+mw_protocol_new(void) {
+  return PURPLE_PROTOCOL(g_object_new(
+    MW_TYPE_PROTOCOL,
+    "id", PROTOCOL_ID,
+    "name", PROTOCOL_NAME,
+    NULL));
+}
+
 static PurplePluginInfo *
 plugin_query(GError **error)
 {
@@ -5401,7 +5417,7 @@
 
   mw_xfer_register_type(G_TYPE_MODULE(plugin));
 
-  my_protocol = g_object_new(MW_TYPE_PROTOCOL, NULL);
+  my_protocol = mw_protocol_new();
   if(!purple_protocol_manager_register(manager, my_protocol, error)) {
     g_clear_object(&my_protocol);
 
diff --git a/libpurple/protocols/zephyr/zephyr.c b/libpurple/protocols/zephyr/zephyr.c
--- a/libpurple/protocols/zephyr/zephyr.c
+++ b/libpurple/protocols/zephyr/zephyr.c
@@ -1257,6 +1257,49 @@
 	return "zephyr";
 }
 
+static GList *
+zephyr_protocol_get_account_options(PurpleProtocol *protocol) {
+	PurpleAccountOption *option;
+	GList *opts = NULL;
+	const gchar *tmp = get_exposure_level();
+
+	option = purple_account_option_bool_new(_("Use tzc"), "use_tzc", FALSE);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_string_new(_("tzc command"), "tzc_command",
+	                                          "/usr/bin/tzc -e %s");
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_bool_new(_("Export to .anyone"),
+	                                        "write_anyone", FALSE);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_bool_new(_("Export to .zephyr.subs"),
+	                                        "write_zsubs", FALSE);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_bool_new(_("Import from .anyone"),
+	                                        "read_anyone", TRUE);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_bool_new(_("Import from .zephyr.subs"),
+	                                        "read_zsubs", TRUE);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_string_new(_("Realm"), "realm", "");
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_string_new(_("Exposure"), "exposure_level",
+	                                          tmp);
+	opts = g_list_append(opts, option);
+
+	option = purple_account_option_string_new(_("Encoding"), "encoding",
+	                                          ZEPHYR_FALLBACK_CHARSET);
+	opts = g_list_append(opts, option);
+
+	return opts;
+}
+
 static unsigned int
 zephyr_send_typing(G_GNUC_UNUSED PurpleProtocolIM *im, PurpleConnection *gc, const char *who, PurpleIMTypingState state)
 {
@@ -1575,41 +1618,6 @@
 static void
 zephyr_protocol_init(ZephyrProtocol *self)
 {
-	PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
-	PurpleAccountOption *option;
-	const gchar *tmp = get_exposure_level();
-
-	protocol->id      = "prpl-zephyr";
-	protocol->name    = "Zephyr";
-	protocol->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_NO_PASSWORD;
-
-	option = purple_account_option_bool_new(_("Use tzc"), "use_tzc", FALSE);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_string_new(_("tzc command"), "tzc_command", "/usr/bin/tzc -e %s");
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_bool_new(_("Export to .anyone"), "write_anyone", FALSE);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_bool_new(_("Export to .zephyr.subs"), "write_zsubs", FALSE);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_bool_new(_("Import from .anyone"), "read_anyone", TRUE);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_bool_new(_("Import from .zephyr.subs"), "read_zsubs", TRUE);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_string_new(_("Realm"), "realm", "");
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_string_new(_("Exposure"), "exposure_level",
-	                                          tmp);
-	protocol->account_options = g_list_append(protocol->account_options, option);
-
-	option = purple_account_option_string_new(_("Encoding"), "encoding", ZEPHYR_FALLBACK_CHARSET);
-	protocol->account_options = g_list_append(protocol->account_options, option);
 }
 
 
@@ -1622,6 +1630,8 @@
 	protocol_class->close = zephyr_close;
 	protocol_class->status_types = zephyr_status_types;
 	protocol_class->list_icon = zephyr_list_icon;
+
+	protocol_class->get_account_options = zephyr_protocol_get_account_options;
 }
 
 
@@ -1688,6 +1698,16 @@
         G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
                                       zephyr_protocol_chat_iface_init));
 
+static PurpleProtocol *
+zephyr_protocol_new(void) {
+	return PURPLE_PROTOCOL(g_object_new(
+		ZEPHYR_TYPE_PROTOCOL,
+		"id", "prpl-zephyr",
+		"name", "Zephyr",
+		"options", OPT_PROTO_CHAT_TOPIC | OPT_PROTO_NO_PASSWORD,
+		NULL));
+}
+
 static PurplePluginInfo *plugin_query(GError **error)
 {
 	return purple_plugin_info_new(
@@ -1713,7 +1733,7 @@
 
 	zephyr_protocol_register_type(G_TYPE_MODULE(plugin));
 
-	my_protocol = g_object_new(ZEPHYR_TYPE_PROTOCOL, NULL);
+	my_protocol = zephyr_protocol_new();
 	if(!purple_protocol_manager_register(manager, my_protocol, error)) {
 		g_clear_object(&my_protocol);
 
diff --git a/libpurple/tests/test_protocol_attention.c b/libpurple/tests/test_protocol_attention.c
--- a/libpurple/tests/test_protocol_attention.c
+++ b/libpurple/tests/test_protocol_attention.c
@@ -79,21 +79,26 @@
 
 static void
 test_purple_protocol_attention_init(TestPurpleProtocolAttention *prplattn) {
-	PurpleProtocol *prpl = PURPLE_PROTOCOL(prplattn);
-
-	prpl->id = "prpl-attention";
 }
 
 static void
 test_purple_protocol_attention_class_init(TestPurpleProtocolAttentionClass *klass) {
 }
 
+static TestPurpleProtocolAttention *
+test_purple_protocol_attention_new(void) {
+	return (TestPurpleProtocolAttention *)(g_object_new(
+		test_purple_protocol_attention_get_type(),
+		"id", "prpl-attention",
+		NULL));
+}
+
 /******************************************************************************
  * Tests
  *****************************************************************************/
 static void
 test_purple_protocol_attention_can_send(void) {
-	TestPurpleProtocolAttention *attn = g_object_new(test_purple_protocol_attention_get_type(), NULL);
+	TestPurpleProtocolAttention *attn = test_purple_protocol_attention_new();
 	PurpleAccount *a = purple_account_new("prpl-attn-can-send", "prpl-attn");
 	PurpleConnection *c = g_object_new(PURPLE_TYPE_CONNECTION, "account", a, NULL);
 	gboolean actual = FALSE;
diff --git a/libpurple/tests/test_protocol_xfer.c b/libpurple/tests/test_protocol_xfer.c
--- a/libpurple/tests/test_protocol_xfer.c
+++ b/libpurple/tests/test_protocol_xfer.c
@@ -88,21 +88,26 @@
 
 static void
 test_purple_protocol_xfer_init(TestPurpleProtocolXfer *prplxfer) {
-	PurpleProtocol *prpl = PURPLE_PROTOCOL(prplxfer);
-
-	prpl->id = "prpl-xfer";
 }
 
 static void
 test_purple_protocol_xfer_class_init(TestPurpleProtocolXferClass *klass) {
 }
 
+static TestPurpleProtocolXfer *
+test_purple_protocol_xfer_new(void) {
+	return (TestPurpleProtocolXfer *)(g_object_new(
+		test_purple_protocol_xfer_get_type(),
+		"id", "prpl-xfer",
+		NULL));
+}
+
 /******************************************************************************
  * Tests
  *****************************************************************************/
 static void
 test_purple_protocol_xfer_can_receive_func(void) {
-	TestPurpleProtocolXfer *xfer = g_object_new(test_purple_protocol_xfer_get_type(), NULL);
+	TestPurpleProtocolXfer *xfer = test_purple_protocol_xfer_new();
 	PurpleAccount *a = purple_account_new("prpl-xfer-can-receive", "prpl-xfer");
 	PurpleConnection *c = g_object_new(PURPLE_TYPE_CONNECTION, "account", a, NULL);
 	gboolean actual = FALSE;
@@ -112,7 +117,7 @@
 	xfer->can_send = FALSE;
 	actual = purple_protocol_xfer_can_receive(
 		PURPLE_PROTOCOL_XFER(xfer),
-		c, 
+		c,
 		"foo"
 	);
 	g_assert_false(actual);
diff --git a/pidgin/gtkaccount.c b/pidgin/gtkaccount.c
--- a/pidgin/gtkaccount.c
+++ b/pidgin/gtkaccount.c
@@ -212,6 +212,8 @@
 		pixbuf = scale;
 	}
 
+	purple_buddy_icon_spec_free(icon_spec);
+
 	if (pixbuf == NULL)
 	{
 		/* Show a placeholder icon */
@@ -574,6 +576,9 @@
 			gtk_entry_set_text(GTK_ENTRY(entry), value);
 	}
 
+	g_list_free_full(user_splits,
+	                 (GDestroyNotify)purple_account_user_split_destroy);
+
 	if (username != NULL)
 		gtk_entry_set_text(GTK_ENTRY(dialog->username_entry), username);
 
@@ -713,6 +718,8 @@
 			gtk_widget_hide(dialog->icon_check);
 			gtk_widget_hide(dialog->icon_hbox);
 		}
+
+		purple_buddy_icon_spec_free(icon_spec);
 	}
 
 	if (dialog->account != NULL) {
@@ -766,7 +773,7 @@
 	PurpleAccountOption *option;
 	PurpleAccount *account;
 	GtkWidget *vbox, *check, *entry, *combo;
-	GList *list, *node;
+	GList *list, *node, *opts;
 	gint i, idx, int_value;
 	GtkListStore *model;
 	GtkTreeIter iter;
@@ -788,9 +795,14 @@
 	g_list_free_full(dialog->protocol_opt_entries, (GDestroyNotify)protocol_opt_entry_free);
 	dialog->protocol_opt_entries = NULL;
 
-	if (dialog->protocol == NULL ||
-			purple_protocol_get_account_options(dialog->protocol) == NULL)
+	if (dialog->protocol == NULL) {
 		return;
+	}
+
+	opts = purple_protocol_get_account_options(dialog->protocol);
+	if(opts == NULL) {
+		return;
+	}
 
 	account = dialog->account;
 
@@ -801,7 +813,7 @@
 			gtk_label_new_with_mnemonic(_("Ad_vanced")), 1);
 	gtk_widget_show(vbox);
 
-	for (l = purple_protocol_get_account_options(dialog->protocol); l != NULL; l = l->next)
+	for (l = opts; l != NULL; l = l->next)
 	{
 		option = (PurpleAccountOption *)l->data;
 
@@ -978,6 +990,7 @@
 			g_list_append(dialog->protocol_opt_entries, opt_entry);
 
 	}
+	g_list_free_full(opts, (GDestroyNotify)purple_account_option_destroy);
 }
 
 static GtkWidget *
@@ -1394,6 +1407,8 @@
 			purple_account_set_buddy_icon_path(account, filename);
 			purple_buddy_icons_set_account_icon(account, data, len);
 		}
+
+		purple_buddy_icon_spec_free(icon_spec);
 	}
 
 
@@ -2165,6 +2180,8 @@
 		} else {
 			img = purple_buddy_icons_find_account_icon(account);
 		}
+
+		purple_buddy_icon_spec_free(icon_spec);
 	}
 
 	if (img != NULL) {
diff --git a/pidgin/gtkblist.c b/pidgin/gtkblist.c
--- a/pidgin/gtkblist.c
+++ b/pidgin/gtkblist.c
@@ -2230,7 +2230,7 @@
 		}
 
 		protocol =
-			purple_protocol_class_list_icon(purple_connection_get_protocol(gc),
+			purple_protocol_list_icon(purple_connection_get_protocol(gc),
 					purple_buddy_get_account(buddy), buddy);
 
 		str = g_string_new(NULL);
@@ -2688,8 +2688,12 @@
 	if (protocol)
 		icon_spec = purple_protocol_get_icon_spec(protocol);
 
-	if (icon_spec && icon_spec->scale_rules & PURPLE_ICON_SCALE_DISPLAY)
-		purple_buddy_icon_spec_get_scaled_size(purple_protocol_get_icon_spec(protocol), &scale_width, &scale_height);
+	if (icon_spec && icon_spec->scale_rules & PURPLE_ICON_SCALE_DISPLAY) {
+		purple_buddy_icon_spec_get_scaled_size(icon_spec, &scale_width,
+		                                       &scale_height);
+	}
+
+	purple_buddy_icon_spec_free(icon_spec);
 
 	if (scaled || scale_height > 200 || scale_width > 200) {
 		GdkPixbuf *tmpbuf;
diff --git a/pidgin/gtkutils.c b/pidgin/gtkutils.c
--- a/pidgin/gtkutils.c
+++ b/pidgin/gtkutils.c
@@ -269,7 +269,7 @@
 	char *filename = NULL;
 	GdkPixbuf *pixbuf;
 
-	protoname = purple_protocol_class_list_icon(protocol, account, NULL);
+	protoname = purple_protocol_list_icon(protocol, account, NULL);
 	if (protoname == NULL)
 		return NULL;
 
@@ -491,7 +491,7 @@
 					proto = purple_connection_get_protocol(gc);
 				}
 
-				protoname = purple_protocol_class_list_icon(proto, account, NULL);
+				protoname = purple_protocol_list_icon(proto, account, NULL);
 
 				if (purple_strequal(protoname, protocol))
 					break;
@@ -1523,11 +1523,18 @@
 	gchar *tmp;
 
 	spec = purple_protocol_get_icon_spec(protocol);
-	g_return_val_if_fail(spec->format != NULL, NULL);
+	if(spec->format == NULL) {
+		purple_buddy_icon_spec_free(spec);
+
+		return NULL;
+	}
 
 	format = gdk_pixbuf_get_file_info(path, &orig_width, &orig_height);
 	if (format == NULL) {
 		purple_debug_warning("buddyicon", "Could not get file info of %s\n", path);
+
+		purple_buddy_icon_spec_free(spec);
+
 		return NULL;
 	}
 
@@ -1545,6 +1552,7 @@
 			purple_debug_warning("buddyicon", "Could not get file contents "
 					"of %s: %s\n", path, error->message);
 			g_strfreev(protocol_formats);
+			purple_buddy_icon_spec_free(spec);
 			return NULL;
 		}
 
@@ -1554,6 +1562,7 @@
 			if (len)
 				*len = length;
 			g_strfreev(protocol_formats);
+			purple_buddy_icon_spec_free(spec);
 			return contents;
 		}
 
@@ -1570,6 +1579,7 @@
 				"conversion: %s\n", path, error->message);
 		g_error_free(error);
 		g_strfreev(protocol_formats);
+		purple_buddy_icon_spec_free(spec);
 		return NULL;
 	}
 	original = g_object_ref(pixbuf);
@@ -1638,6 +1648,7 @@
 					g_strfreev(protocol_formats);
 					g_object_unref(G_OBJECT(pixbuf));
 					g_object_unref(G_OBJECT(original));
+					purple_buddy_icon_spec_free(spec);
 					return contents;
 				}
 
@@ -1672,6 +1683,8 @@
 	purple_notify_error(NULL, _("Icon Error"), _("Could not set icon"), tmp, NULL);
 	g_free(tmp);
 
+	purple_buddy_icon_spec_free(spec);
+
 	return NULL;
 }
 
