# HG changeset patch
# Node ID 4962121a548b
# Parent  26ae6ffab6d0
diff --git a/protocols/xmpp/meson.build b/protocols/xmpp/meson.build
--- a/protocols/xmpp/meson.build
+++ b/protocols/xmpp/meson.build
@@ -1,9 +1,12 @@
 XMPP_SOURCES = [
+	'purplexmppconnection.c',
 	'purplexmppcore.c',
 	'purplexmppprotocol.c',
 ]
 
 XMPP_HEADERS = [
+	'purplexmppconnection.h',
+	'purplexmppconstants.h',
 	'purplexmppcore.h',
 	'purplexmppprotocol.h',
 ]
diff --git a/protocols/xmpp/purplexmppconnection.h b/protocols/xmpp/purplexmppconnection.h
new file mode 100644
--- /dev/null
+++ b/protocols/xmpp/purplexmppconnection.h
@@ -0,0 +1,48 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef PURPLE_XMPP_CONNECTION_H
+#define PURPLE_XMPP_CONNECTION_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gplugin.h>
+#include <gplugin-native.h>
+
+#include <purple.h>
+
+G_BEGIN_DECLS
+
+#define PURPLE_XMPP_TYPE_CONNECTION (purple_xmpp_connection_get_type())
+
+G_DECLARE_FINAL_TYPE(PurpleXmppConnection, purple_xmpp_connection, PURPLE_XMPP,
+                     CONNECTION, PurpleConnection)
+
+/**
+ * purple_xmpp_connection_register: (skip)
+ * @plugin: The GTypeModule
+ *
+ * Registers the dynamic type using @plugin.
+ *
+ * Since: 3.0.0
+ */
+G_GNUC_INTERNAL void purple_xmpp_connection_register(GPluginNativePlugin *plugin);
+
+G_END_DECLS
+
+#endif /* PURPLE_XMPP_CONNECTION_H */
diff --git a/protocols/xmpp/purplexmppconnection.c b/protocols/xmpp/purplexmppconnection.c
new file mode 100644
--- /dev/null
+++ b/protocols/xmpp/purplexmppconnection.c
@@ -0,0 +1,210 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include <xeme.h>
+
+#include "purplexmppconnection.h"
+
+#include "purplexmppconstants.h"
+#include "purplexmppcore.h"
+
+struct _PurpleXmppConnection {
+	PurpleConnection parent;
+
+	XemeConnection *xeme_connection;
+	XemeOutputStream *output;
+};
+
+G_DEFINE_DYNAMIC_TYPE(PurpleXmppConnection, purple_xmpp_connection,
+                      PURPLE_TYPE_CONNECTION)
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+purple_xmpp_connection_connect_cb(GObject *source, GAsyncResult *result,
+                                  gpointer data)
+{
+	PurpleXmppConnection *connection = data;
+	XemeOutputStream *output = NULL;
+	GError *error = NULL;
+
+	output = xeme_connection_connect_finish(XEME_CONNECTION(source), result,
+	                                        &error);
+	if(error != NULL) {
+		purple_connection_take_error(PURPLE_CONNECTION(connection), error);
+
+		return;
+	}
+
+	connection->output = output;
+
+	purple_connection_set_state(PURPLE_CONNECTION(connection),
+	                            PURPLE_CONNECTION_STATE_CONNECTED);
+}
+
+/******************************************************************************
+ * PurpleConnection Implementation
+ *****************************************************************************/
+static gboolean
+purple_xmpp_connection_connect(PurpleConnection *purple_connection,
+                               GError **error)
+{
+	PurpleXmppConnection *connection = NULL;
+	PurpleAccount *account = NULL;
+	XemeInputStream *input = NULL;
+	XemeTlsMode xeme_tls_mode = XEME_TLS_MODE_DIRECT_TLS;
+	GCancellable *cancellable = NULL;
+	GError *local_error = NULL;
+	GProxyResolver *resolver = NULL;
+	const char *resource = NULL;
+	const char *server = NULL;
+	const char *tls_mode = NULL;
+	const char *username = NULL;
+	guint16 port = PURPLE_XMPP_DEFAULT_PORT_TLS;
+
+	g_return_val_if_fail(PURPLE_XMPP_IS_CONNECTION(purple_connection), FALSE);
+
+	purple_connection_set_state(purple_connection,
+	                            PURPLE_CONNECTION_STATE_CONNECTING);
+
+	connection = PURPLE_XMPP_CONNECTION(purple_connection);
+	cancellable = purple_connection_get_cancellable(purple_connection);
+	account = purple_connection_get_account(purple_connection);
+
+	/* Figure out what proxy resolver to use. */
+	resolver = purple_proxy_get_proxy_resolver(account, &local_error);
+	if(!G_IS_PROXY_RESOLVER(resolver) || local_error != NULL) {
+		if(local_error == NULL) {
+			local_error = g_error_new(PURPLE_XMPP_DOMAIN, 0, "unknown error");
+		}
+
+		g_propagate_error(error, local_error);
+
+		return FALSE;
+	}
+
+	/* Grab some more information that we need. */
+	username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(account));
+	connection->xeme_connection = xeme_connection_new(username);
+
+	/* If we have a resource, tell the xeme connection about it. */
+	resource = purple_account_get_string(account, PURPLE_XMPP_OPTION_RESOURCE,
+	                                     NULL);
+	if(!purple_strempty(resource)) {
+		xeme_connection_set_resource(connection->xeme_connection, resource);
+	}
+
+	/* If we have a server, tell the xeme connection about it. */
+	server = purple_account_get_string(account, PURPLE_XMPP_OPTION_SERVER,
+	                                   NULL);
+	if(!purple_strempty(server)) {
+		xeme_connection_set_server(connection->xeme_connection, server);
+	}
+
+	/* Set whatever port we have on our xeme connection. */
+	port = purple_account_get_int(account, PURPLE_XMPP_OPTION_PORT,
+	                              PURPLE_XMPP_DEFAULT_PORT_TLS);
+	xeme_connection_set_port(connection->xeme_connection, port);
+
+	/* Figure out what if any TLS we're doing. This is initialized to direct
+	 * tls above, so if we don't know which to use, that's what we'll end up
+	 * using. Doing so should avoid possible accidental leaks.
+	 */
+	tls_mode = purple_account_get_string(account, PURPLE_XMPP_OPTION_TLS_MODE,
+	                                     PURPLE_XMPP_TLS_DIRECT);
+	if(purple_strequal(tls_mode, PURPLE_XMPP_TLS_DIRECT)) {
+		xeme_tls_mode = XEME_TLS_MODE_DIRECT_TLS;
+	} else if(purple_strequal(tls_mode, PURPLE_XMPP_TLS_STARTTLS)) {
+		xeme_tls_mode = XEME_TLS_MODE_START_TLS;
+	} else if(purple_strequal(tls_mode, PURPLE_XMPP_TLS_NONE)) {
+		xeme_tls_mode = XEME_TLS_MODE_NONE;
+	}
+
+	xeme_connection_set_tls_mode(connection->xeme_connection, xeme_tls_mode);
+
+	input = xeme_input_stream_new();
+
+	/* Finally start connecting!! */
+	xeme_connection_connect_async(connection->xeme_connection, input, resolver,
+	                              cancellable, purple_xmpp_connection_connect_cb,
+	                              connection);
+
+	g_clear_object(&input);
+
+	return TRUE;
+}
+
+static gboolean
+purple_xmpp_connection_disconnect(PurpleConnection *purple_connection,
+                                  GError **error)
+{
+	PurpleXmppConnection *connection = NULL;
+	PurpleConnectionClass *parent_class = NULL;
+
+	g_return_val_if_fail(PURPLE_XMPP_IS_CONNECTION(purple_connection), FALSE);
+
+	connection = PURPLE_XMPP_CONNECTION(purple_connection);
+
+	/* Chain up to our parent's disconnect method. */
+	parent_class = PURPLE_CONNECTION_CLASS(purple_xmpp_connection_parent_class);
+	parent_class->disconnect(purple_connection, error);
+
+	return xeme_connection_close(connection->xeme_connection, error);
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+purple_xmpp_connection_dispose(GObject *obj) {
+	PurpleXmppConnection *connection = PURPLE_XMPP_CONNECTION(obj);
+
+	g_clear_object(&connection->output);
+
+	G_OBJECT_CLASS(purple_xmpp_connection_parent_class)->dispose(obj);
+}
+
+static void
+purple_xmpp_connection_init(G_GNUC_UNUSED PurpleXmppConnection *connection) {
+}
+
+static void
+purple_xmpp_connection_class_init(PurpleXmppConnectionClass *klass) {
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+	PurpleConnectionClass *connection_class = PURPLE_CONNECTION_CLASS(klass);
+
+	obj_class->dispose = purple_xmpp_connection_dispose;
+
+	connection_class->connect = purple_xmpp_connection_connect;
+	connection_class->disconnect = purple_xmpp_connection_disconnect;
+}
+
+static void
+purple_xmpp_connection_class_finalize(G_GNUC_UNUSED PurpleXmppConnectionClass *klass) {
+}
+
+/******************************************************************************
+ * Internal API
+ *****************************************************************************/
+void
+purple_xmpp_connection_register(GPluginNativePlugin *plugin) {
+	purple_xmpp_connection_register_type(G_TYPE_MODULE(plugin));
+}
diff --git a/protocols/xmpp/purplexmppconstants.h b/protocols/xmpp/purplexmppconstants.h
new file mode 100644
--- /dev/null
+++ b/protocols/xmpp/purplexmppconstants.h
@@ -0,0 +1,36 @@
+/*
+ * Purple - Internet Messaging Library
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef PURPLE_XMPP_CONSTANTS_H
+#define PURPLE_XMPP_CONSTANTS_H
+
+#define PURPLE_XMPP_DEFAULT_PORT_CLEAR (5222)
+#define PURPLE_XMPP_DEFAULT_PORT_TLS (5223)
+
+#define PURPLE_XMPP_OPTION_RESOURCE ("resource")
+#define PURPLE_XMPP_OPTION_SERVER ("server")
+#define PURPLE_XMPP_OPTION_PORT ("port")
+#define PURPLE_XMPP_OPTION_TLS_MODE ("tls-mode")
+#define PURPLE_XMPP_OPTION_SASL_LOGIN_NAME ("sasl-login-name")
+#define PURPLE_XMPP_OPTION_SASL_MECHANISMS ("sasl-mechanisms")
+#define PURPLE_XMPP_OPTION_PLAIN_SASL_IN_CLEAR ("plain-sasl-in-clear")
+
+#define PURPLE_XMPP_TLS_DIRECT ("direct")
+#define PURPLE_XMPP_TLS_STARTTLS ("starttls")
+#define PURPLE_XMPP_TLS_NONE ("none")
+
+#endif /* PURPLE_XMPP_CONSTANTS_H */
diff --git a/protocols/xmpp/purplexmppcore.c b/protocols/xmpp/purplexmppcore.c
--- a/protocols/xmpp/purplexmppcore.c
+++ b/protocols/xmpp/purplexmppcore.c
@@ -26,6 +26,7 @@
 
 #include "purplexmppcore.h"
 
+#include "purplexmppconnection.h"
 #include "purplexmppprotocol.h"
 
 /******************************************************************************
@@ -71,6 +72,7 @@
 	}
 
 	purple_xmpp_protocol_register(GPLUGIN_NATIVE_PLUGIN(plugin));
+	purple_xmpp_connection_register(GPLUGIN_NATIVE_PLUGIN(plugin));
 
 	xmpp_protocol = purple_xmpp_protocol_new();
 
diff --git a/protocols/xmpp/purplexmppprotocol.c b/protocols/xmpp/purplexmppprotocol.c
--- a/protocols/xmpp/purplexmppprotocol.c
+++ b/protocols/xmpp/purplexmppprotocol.c
@@ -20,6 +20,9 @@
 
 #include "purplexmppprotocol.h"
 
+#include "purplexmppconnection.h"
+#include "purplexmppconstants.h"
+
 struct _PurpleXmppProtocol {
 	PurpleProtocol parent;
 };
@@ -39,6 +42,59 @@
 }
 
 static GList *
+purple_xmpp_protocol_get_account_options(G_GNUC_UNUSED PurpleProtocol *protocol)
+{
+	PurpleAccountOption *option = NULL;
+	PurpleKeyValuePair *pair = NULL;
+	GList *options = NULL;
+	GList *items = NULL;
+
+	option = purple_account_option_string_new(_("Resource"),
+	                                          PURPLE_XMPP_OPTION_RESOURCE,
+	                                          NULL);
+	options = g_list_append(options, option);
+
+	option = purple_account_option_string_new(_("Server"),
+	                                          PURPLE_XMPP_OPTION_SERVER, NULL);
+	options = g_list_append(options, option);
+
+	option = purple_account_option_int_new(_("Port"), PURPLE_XMPP_OPTION_PORT,
+	                                       PURPLE_XMPP_DEFAULT_PORT_TLS);
+	options = g_list_append(options, option);
+
+	pair = purple_key_value_pair_new(_("Direct TLS"), PURPLE_XMPP_TLS_DIRECT);
+	items = g_list_append(items, pair);
+	pair = purple_key_value_pair_new(_("STARTTLS"), PURPLE_XMPP_TLS_STARTTLS);
+	items = g_list_append(items, pair);
+	pair = purple_key_value_pair_new(_("None"), PURPLE_XMPP_TLS_NONE);
+	items = g_list_append(items, pair);
+
+	option = purple_account_option_list_new(_("TLS Mode"),
+	                                        PURPLE_XMPP_OPTION_TLS_MODE,
+	                                        items);
+	options = g_list_append(options, option);
+
+	option = purple_account_option_string_new(_("SASL login name"),
+	                                          PURPLE_XMPP_OPTION_SASL_LOGIN_NAME,
+	                                          NULL);
+	options = g_list_append(options, option);
+
+	option = purple_account_option_string_new(_("SASL mechanisms"),
+	                                          PURPLE_XMPP_OPTION_SASL_MECHANISMS,
+	                                          NULL);
+	options = g_list_append(options, option);
+
+	option = purple_account_option_bool_new(_("Allow plaintext SASL auth over "
+	                                          "unencrypted connection"),
+	                                        PURPLE_XMPP_OPTION_PLAIN_SASL_IN_CLEAR,
+	                                        FALSE);
+	options = g_list_append(options, option);
+
+	return options;
+}
+
+
+static GList *
 purple_xmpp_protocol_status_types(G_GNUC_UNUSED PurpleProtocol *protocol,
                                   G_GNUC_UNUSED PurpleAccount *account)
 {
@@ -54,6 +110,20 @@
 	return types;
 }
 
+static PurpleConnection *
+purple_xmpp_protocol_create_connection(PurpleProtocol *protocol,
+                                       PurpleAccount *account,
+                                       const char *password,
+                                       G_GNUC_UNUSED GError** error)
+{
+	return g_object_new(
+		PURPLE_XMPP_TYPE_CONNECTION,
+		"protocol", protocol,
+		"account", account,
+		"password", password,
+		NULL);
+}
+
 /******************************************************************************
  * GObject Implementation
  *****************************************************************************/
@@ -77,7 +147,10 @@
 	PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
 
 	protocol_class->get_user_splits = purple_xmpp_protocol_get_user_splits;
+	protocol_class->get_account_options = purple_xmpp_protocol_get_account_options;
 	protocol_class->status_types = purple_xmpp_protocol_status_types;
+
+	protocol_class->create_connection = purple_xmpp_protocol_create_connection;
 }
 
 /******************************************************************************
diff --git a/subprojects/xeme.wrap b/subprojects/xeme.wrap
--- a/subprojects/xeme.wrap
+++ b/subprojects/xeme.wrap
@@ -1,4 +1,4 @@
 [wrap-hg]
 directory = xeme
 url = https://keep.imfreedom.org/xeme/xeme/
-revision = 6f495ad383bb
+revision = 19f8cef54b13
