Index: lib/ssl_client.h
===================================================================
--- lib/ssl_client.h	(revision 172a73f1a4b37fa20d1d50496a3faccb8fe6c769)
+++ lib/ssl_client.h	(revision 42127dcd26be4f6746004237eac5333ffbb94f8e)
@@ -52,4 +52,8 @@
 G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data );
 
+/* Start an SSL session on an existing fd. Useful for STARTTLS functionality,
+   for example in Jabber. */
+G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer data );
+
 /* Obviously you need special read/write functions to read data. */
 G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len );
Index: lib/ssl_gnutls.c
===================================================================
--- lib/ssl_gnutls.c	(revision 172a73f1a4b37fa20d1d50496a3faccb8fe6c769)
+++ lib/ssl_gnutls.c	(revision 42127dcd26be4f6746004237eac5333ffbb94f8e)
@@ -63,5 +63,41 @@
 	{
 		g_free( conn );
-		return( NULL );
+		return NULL;
+	}
+	
+	return conn;
+}
+
+/* FIXME: It can happen that the handshake fails even before ssl_connected()
+   returns already. This function will then return an invalid pointer because
+   these failures can't be detected properly yet. Maybe ssl_connected()
+   shouldn't be called directly, but via a short timeout? */
+void *ssl_starttls( int fd, ssl_input_function func, gpointer data )
+{
+	struct scd *conn = g_new0( struct scd, 1 );
+	
+	conn->fd = fd;
+	conn->func = func;
+	conn->data = data;
+	conn->inpa = -1;
+	
+	ssl_connected( conn, fd, GAIM_INPUT_WRITE );
+	
+	return conn;
+}
+
+static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
+
+static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
+{
+	struct scd *conn = data;
+	
+	if( source == -1 )
+	{
+		conn->func( conn->data, NULL, cond );
+		
+		g_free( conn );
+		
+		return FALSE;
 	}
 	
@@ -77,25 +113,4 @@
 	gnutls_set_default_priority( conn->session );
 	gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred );
-	
-	return( conn );
-}
-
-static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
-
-static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
-{
-	struct scd *conn = data;
-	
-	if( source == -1 )
-	{
-		conn->func( conn->data, NULL, cond );
-		
-		gnutls_deinit( conn->session );
-		gnutls_certificate_free_credentials( conn->xcred );
-		
-		g_free( conn );
-		
-		return FALSE;
-	}
 	
 	sock_make_nonblocking( conn->fd );
Index: protocols/jabber/io.c
===================================================================
--- protocols/jabber/io.c	(revision 172a73f1a4b37fa20d1d50496a3faccb8fe6c769)
+++ protocols/jabber/io.c	(revision 42127dcd26be4f6746004237eac5333ffbb94f8e)
@@ -23,4 +23,5 @@
 
 #include "jabber.h"
+#include "ssl_client.h"
 
 static gboolean jabber_write_callback( gpointer data, gint fd, b_input_condition cond );
@@ -76,5 +77,10 @@
 		return FALSE;
 	
-	st = write( jd->fd, jd->txq, jd->tx_len );
+	if( jd->ssl )
+		st = ssl_write( jd->ssl, jd->txq, jd->tx_len );
+	else
+		st = write( jd->fd, jd->txq, jd->tx_len );
+	
+//	if( st > 0 ) write( 1, jd->txq, st );
 	
 	if( st == jd->tx_len )
@@ -126,5 +132,10 @@
 		return FALSE;
 	
-	st = read( fd, buf, sizeof( buf ) );
+	if( jd->ssl )
+		st = ssl_read( jd->ssl, buf, sizeof( buf ) );
+	else
+		st = read( jd->fd, buf, sizeof( buf ) );
+	
+//	if( st > 0 ) write( 1, buf, st );
 	
 	if( st > 0 )
@@ -210,4 +221,20 @@
 }
 
+gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond )
+{
+	struct gaim_connection *gc = data;
+	
+	if( source == NULL )
+	{
+		hide_login_progress( gc, "Could not connect to server" );
+		signoff( gc );
+		return FALSE;
+	}
+	
+	set_login_progress( gc, 1, "Connected to server, logging in" );
+	
+	return jabber_start_stream( gc );
+}
+
 static xt_status jabber_end_of_stream( struct xt_node *node, gpointer data )
 {
@@ -222,12 +249,40 @@
 	
 	c = xt_find_node( node->children, "starttls" );
-	if( c )
-	{
-		/*
-		jd->flags |= JFLAG_SUPPORTS_TLS;
-		if( xt_find_node( c->children, "required" ) )
-			jd->flags |= JFLAG_REQUIRES_TLS;
-		*/
-	}
+	if( c && !jd->ssl )
+	{
+		/* If the server advertises the STARTTLS feature and if we're
+		   not in a secure connection already: */
+		
+		int try;
+		
+		try = g_strcasecmp( set_getstr( &gc->acc->set, "tls" ), "try" ) == 0;
+		c = xt_find_node( c->children, "required" );
+		
+		/* Only run this if the tls setting is set to true or try: */
+		if( ( try | set_getbool( &gc->acc->set, "tls" ) ) )
+		{
+			reply = xt_new_node( "starttls", NULL, NULL );
+			xt_add_attr( reply, "xmlns", "urn:ietf:params:xml:ns:xmpp-tls" );
+			if( !jabber_write_packet( gc, reply ) )
+			{
+				xt_free_node( reply );
+				return XT_ABORT;
+			}
+			xt_free_node( reply );
+			
+			return XT_HANDLED;
+		}
+	}
+	else
+	{
+		/* TODO: Abort if TLS is required by the user. */
+	}
+	
+	/* This one used to be in jabber_handlers[], but it has to be done
+	   from here to make sure the TLS session will be initialized
+	   properly before we attempt SASL authentication. */
+	if( ( c = xt_find_node( node->children, "mechanisms" ) ) )
+		if( sasl_pkt_mechanisms( c, data ) == XT_ABORT )
+			return XT_ABORT;
 	
 	if( ( c = xt_find_node( node->children, "bind" ) ) )
@@ -265,4 +320,38 @@
 			return XT_ABORT;
 	}
+	
+	return XT_HANDLED;
+}
+
+static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data )
+{
+	struct gaim_connection *gc = data;
+	struct jabber_data *jd = gc->proto_data;
+	char *xmlns;
+	
+	xmlns = xt_find_attr( node, "xmlns" );
+	
+	/* Just ignore it when it doesn't seem to be TLS-related (is that at
+	   all possible??). */
+	if( !xmlns || strcmp( xmlns, "urn:ietf:params:xml:ns:xmpp-tls" ) != 0 )
+		return XT_HANDLED;
+	
+	/* We don't want event handlers to touch our TLS session while it's
+	   still initializing! */
+	b_event_remove( jd->r_inpa );
+	if( jd->tx_len > 0 )
+	{
+		/* Actually the write queue should be empty here, but just
+		   to be sure... */
+		b_event_remove( jd->w_inpa );
+		g_free( jd->txq );
+		jd->txq = NULL;
+		jd->tx_len = 0;
+	}
+	jd->w_inpa = jd->r_inpa = 0;
+	
+	set_login_progress( gc, 1, "Converting stream to TLS" );
+	
+	jd->ssl = ssl_starttls( jd->fd, jabber_connected_ssl, gc );
 	
 	return XT_HANDLED;
@@ -283,5 +372,5 @@
 	{ "iq",                 "stream:stream",        jabber_pkt_iq },
 	{ "stream:features",    "stream:stream",        jabber_pkt_features },
-	{ "mechanisms",         "stream:features",      sasl_pkt_mechanisms },
+	{ "proceed",            "stream:stream",        jabber_pkt_proceed_tls },
 	{ "challenge",          "stream:stream",        sasl_pkt_challenge },
 	{ "success",            "stream:stream",        sasl_pkt_result },
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision 172a73f1a4b37fa20d1d50496a3faccb8fe6c769)
+++ protocols/jabber/jabber.c	(revision 42127dcd26be4f6746004237eac5333ffbb94f8e)
@@ -80,6 +80,6 @@
 	if( set_getbool( &acc->set, "ssl" ) )
 	{
-		signoff( gc );
-		/* TODO! */
+		jd->ssl = ssl_connect( jd->server, set_getint( &acc->set, "port" ), jabber_connected_ssl, gc );
+		jd->fd = ssl_getfd( jd->ssl );
 	}
 	else
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision 172a73f1a4b37fa20d1d50496a3faccb8fe6c769)
+++ protocols/jabber/jabber.h	(revision 42127dcd26be4f6746004237eac5333ffbb94f8e)
@@ -95,4 +95,5 @@
 int jabber_write( struct gaim_connection *gc, char *buf, int len );
 gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond );
+gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond );
 gboolean jabber_start_stream( struct gaim_connection *gc );
 void jabber_end_stream( struct gaim_connection *gc );
