# HG changeset patch
# Node ID 052c9a314399
# Parent  95d36c221e21
diff --git a/protocols/xmpp/meson.build b/protocols/xmpp/meson.build
--- a/protocols/xmpp/meson.build
+++ b/protocols/xmpp/meson.build
@@ -1,11 +1,15 @@
 XMPP_SOURCES = [
+	'purplexmppconnection.c',
 	'purplexmppcore.c',
 	'purplexmppprotocol.c',
 ]
 
 XMPP_HEADERS = [
+	'purplexmppconnection.h',
+	'purplexmppconstants.h',
 	'purplexmppcore.h',
 	'purplexmppprotocol.h',
+	'purplexmppxeps.h',
 ]
 
 if not DYNAMIC_XMPP
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,226 @@
+/*
+ * 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 "purplexmppconnection.h"
+
+#include "purplexmppconstants.h"
+#include "purplexmppxeps.h"
+
+struct _PurpleXmppConnection {
+	PurpleConnection parent;
+
+	char *username;
+	char *domain;
+	char *resource;
+
+	GSocketConnection *connection;
+
+	GDataInputStream *input;
+	PurpleQueuedOutputStream *output;
+};
+
+G_DEFINE_DYNAMIC_TYPE(PurpleXmppConnection, purple_xmpp_connection,
+                      PURPLE_TYPE_CONNECTION)
+
+/******************************************************************************
+ * Helpers
+ *****************************************************************************/
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+purple_xmpp_connection_connected_cb(GObject *source, GAsyncResult *result,
+                                    gpointer data)
+{
+	PurpleXmppConnection *connection = data;
+	GError *error = NULL;
+	GSocketConnection *socket_connection = NULL;
+
+	socket_connection = g_socket_client_connect_to_service_finish(G_SOCKET_CLIENT(source),
+	                                                              result,
+	                                                              &error);
+
+	if(socket_connection == NULL) {
+		purple_connection_take_error(PURPLE_CONNECTION(connection), error);
+		return;
+	}
+
+	g_warning("connection established");
+}
+
+/******************************************************************************
+ * PurpleConnection Implementation
+ *****************************************************************************/
+static gboolean
+purple_xmpp_connection_connect(PurpleConnection *purple_connection,
+                               GError **error)
+{
+	PurpleXmppConnection *connection = NULL;
+	PurpleAccount *account = NULL;
+	GCancellable *cancellable = NULL;
+	GSocketClient *client = NULL;
+	gboolean use_tls = TRUE;
+
+	g_return_val_if_fail(PURPLE_XMPP_IS_CONNECTION(purple_connection), FALSE);
+
+	connection = PURPLE_XMPP_CONNECTION(purple_connection);
+	account = purple_connection_get_account(purple_connection);
+
+	client = purple_gio_socket_client_new(account, error);
+	if(!G_IS_SOCKET_CLIENT(client)) {
+		if(error != NULL && *error != NULL) {
+			purple_connection_take_error(purple_connection, *error);
+		}
+
+		return FALSE;
+	}
+
+	cancellable = purple_connection_get_cancellable(purple_connection);
+
+	/* Turn on TLS if requested. */
+	use_tls = purple_account_get_bool(account, "use-tls", TRUE);
+	g_socket_client_set_tls(client, use_tls);
+
+	/* Finally start the connection process by trying SRV records first. The
+	 * other attempts are triggered via the callbacks when necessary.
+	 */
+	g_socket_client_connect_to_service_async(client, connection->domain,
+	                                         PURPLE_XMPP_SRV_XMPP_CLIENT,
+	                                         cancellable,
+	                                         purple_xmpp_connection_connected_cb,
+	                                         connection);
+
+	g_clear_object(&client);
+
+	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);
+
+	if(G_IS_SOCKET_CONNECTION(connection->connection)) {
+		GInputStream *istream = G_INPUT_STREAM(connection->input);
+		GOutputStream *ostream = G_OUTPUT_STREAM(connection->output);
+
+		purple_gio_graceful_close(G_IO_STREAM(connection->connection),
+		                          istream, ostream);
+	}
+
+	g_clear_object(&connection->input);
+	g_clear_object(&connection->output);
+	g_clear_object(&connection->connection);
+
+	return TRUE;
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+purple_xmpp_connection_constructed(GObject *obj) {
+	PurpleXmppConnection *connection = PURPLE_XMPP_CONNECTION(obj);
+	PurpleAccount *account = NULL;
+	char **userparts = NULL;
+	const char *username = NULL;
+
+	G_OBJECT_CLASS(purple_xmpp_connection_parent_class)->constructed(obj);
+
+	account = purple_connection_get_account(PURPLE_CONNECTION(connection));
+
+	/* Split the username into nick and server and store the values. */
+	username = purple_contact_info_get_username(PURPLE_CONTACT_INFO(account));
+	userparts = g_strsplit(username, "@", 2);
+	purple_connection_set_display_name(PURPLE_CONNECTION(connection),
+	                                   userparts[0]);
+
+	connection->username = g_strdup(userparts[0]);
+	connection->domain = g_strdup(userparts[1]);
+
+	/* Free the userparts vector. */
+	g_strfreev(userparts);
+}
+
+static void
+purple_xmpp_connection_dispose(GObject *obj) {
+	PurpleXmppConnection *connection = PURPLE_XMPP_CONNECTION(obj);
+
+	g_clear_object(&connection->input);
+	g_clear_object(&connection->output);
+	g_clear_object(&connection->connection);
+
+	G_OBJECT_CLASS(purple_xmpp_connection_parent_class)->dispose(obj);
+}
+
+static void
+purple_xmpp_connection_finalize(GObject *obj) {
+	PurpleXmppConnection *connection = PURPLE_XMPP_CONNECTION(obj);
+
+	g_clear_pointer(&connection->username, g_free);
+	g_clear_pointer(&connection->domain, g_free);
+	g_clear_pointer(&connection->resource, g_free);
+
+	G_OBJECT_CLASS(purple_xmpp_connection_parent_class)->finalize(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->constructed = purple_xmpp_connection_constructed;
+	obj_class->dispose = purple_xmpp_connection_dispose;
+	obj_class->finalize = purple_xmpp_connection_finalize;
+
+	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));
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
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/protocols/xmpp/purplexmppxeps.h b/protocols/xmpp/purplexmppxeps.h
new file mode 100644
--- /dev/null
+++ b/protocols/xmpp/purplexmppxeps.h
@@ -0,0 +1,27 @@
+/*
+ * 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_XEPS_H
+#define PURPLE_XMPP_XEPS_H
+
+/* https://www.rfc-editor.org/rfc/rfc6120#section-3.2.1 */
+#define PURPLE_XMPP_SRV_XMPP_CLIENT ("xmpp-client")
+
+/* https://xmpp.org/extensions/xep-0368.html */
+#define PURPLE_XMPP_SRV_XMPPS_CLIENT ("xmpps-client")
+
+#endif /* PURPLE_XMPP_XEPS_H */
