summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/common/hexchat.h3
-rw-r--r--src/common/proto-irc.c54
-rw-r--r--src/common/servlist.c29
-rw-r--r--src/common/servlist.h1
-rw-r--r--src/fe-gtk/servlistgui.c12
5 files changed, 89 insertions, 10 deletions
diff --git a/src/common/hexchat.h b/src/common/hexchat.h
index ff7a8bf9..9b5ebf04 100644
--- a/src/common/hexchat.h
+++ b/src/common/hexchat.h
@@ -485,6 +485,8 @@ typedef struct server
 	char hostname[128];				/* real ip number */
 	char servername[128];			/* what the server says is its name */
 	char password[86];
+	char sasluser[30];				/* this is just a buffer for network->user */
+	char saslpassword[86];			/* we could reuse password but then we couldn't guarantee NickServ doesn't register first */
 	char nick[NICKLEN];
 	char linebuf[2048];				/* RFC says 512 chars including \r\n */
 	char *last_away_reason;
@@ -547,6 +549,7 @@ typedef struct server
 	unsigned int have_whox:1;		/* have undernet's WHOX features */
 	unsigned int have_capab:1;		/* supports CAPAB (005 tells us) */
 	unsigned int have_idmsg:1;		/* freenode's IDENTIFY-MSG */
+	unsigned int have_sasl:1;		/* SASL capability */
 	unsigned int have_except:1;	/* ban exemptions +e */
 	unsigned int using_cp1255:1;	/* encoding is CP1255/WINDOWS-1255? */
 	unsigned int using_irc:1;		/* encoding is "IRC" (CP1252/UTF-8 hybrid)? */
diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c
index 342ad221..f6e7eccf 100644
--- a/src/common/proto-irc.c
+++ b/src/common/proto-irc.c
@@ -28,6 +28,8 @@
 #include <unistd.h>
 #endif
 
+#include <glib.h>
+
 #include "hexchat.h"
 #include "ctcp.h"
 #include "fe.h"
@@ -47,11 +49,12 @@
 static void
 irc_login (server *serv, char *user, char *realname)
 {
+	tcp_sendf (serv, "CAP LS\r\n");		/* start with CAP LS as Charybdis sasl.txt suggests */
+
 	if (serv->password[0])
+	{
 		tcp_sendf (serv, "PASS %s\r\n", serv->password);
-#if 0	/* breaks the SASL plugin */
-	tcp_sendf (serv, "CAP LS\r\n");
-#endif
+	}
 
 	tcp_sendf (serv,
 				  "NICK %s\r\n"
@@ -880,6 +883,15 @@ process_numeric (session * sess, int n,
 		notify_set_online (serv, word[4]);
 		break;
 
+	case 903:	/* successful SASL auth */
+	case 904:	/* aborted SASL auth */
+	case 905:	/* failed SASL auth */
+	case 906:	/* registration completes before SASL auth */
+	case 907:	/* attempting to re-auth after a successful auth */
+		tcp_send_len (serv, "CAP END\r\n", 9);
+		PrintTextf (sess, "%s\n", ++word_eol[4]);
+		break;
+
 	default:
 
 		if (serv->inside_whois && word[4][0])
@@ -1117,10 +1129,12 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 		}
 	}
 
-#if 0	/* breaks the SASL plugin */
 	else if (len == 3)
 	{
 		guint32 t;
+		int passlen;
+		char *encoded;
+		char *buffer;
 
 		t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]);
 		switch (t)
@@ -1131,28 +1145,54 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 					if (strncasecmp (word[5][0]==':' ? word[5] + 1 : word[5], "identify-msg", 12) == 0)
 					{
 						serv->have_idmsg = TRUE;
-						tcp_send_len (serv, "CAP END\r\n", 9);
+					}
+					if (strncasecmp (word[5][0]==':' ? word[5] + 1 : word[5], "sasl", 12) == 0)
+					{
+						serv->have_sasl = TRUE;
+						PrintTextf (sess, "Authenticating via SASL as %s\n", sess->server->sasluser);
+						tcp_send_len (serv, "AUTHENTICATE PLAIN\r\n", 20);
+
+						/* passphrase generation, nicely copy-pasted from the SASL plugin */
+						passlen = strlen (sess->server->sasluser) * 2 + 2 + strlen (sess->server->saslpassword);
+						buffer = (char*) malloc (passlen + 1);
+						strcpy (buffer, sess->server->sasluser);
+						strcpy (buffer + strlen (sess->server->sasluser) + 1, sess->server->sasluser);
+						strcpy (buffer + strlen (sess->server->sasluser) * 2 + 2, sess->server->saslpassword);
+						encoded = g_base64_encode ((unsigned char*) buffer, passlen);
+
+						tcp_sendf (sess->server, "AUTHENTICATE %s\r\n", encoded);
+
+						free (encoded);
+						free (buffer);
 					}
 				}
 				else if (strncasecmp (word[4], "LS", 2) == 0)
 				{
+					PrintTextf (sess, "Capabilities supported by the server: %s\n", ++word_eol[5]);
 					if (strstr (word_eol[5], "identify-msg") != 0)
 					{
 						tcp_send_len (serv, "CAP REQ :identify-msg\r\n", 23);
 					}
+
+					/* if the SASL password is set, request SASL auth */
+					if (strstr (word_eol[5], "sasl") != 0 && strlen (sess->server->saslpassword) != 0)
+					{
+						tcp_send_len (serv, "CAP REQ :sasl\r\n", 23);
+					}
 					else
 					{
+						/* if we use SASL, CAP END is dealt via raw numerics */
 						tcp_send_len (serv, "CAP END\r\n", 9);
 					}
 				}
-				else if (strncasecmp (word[4], "NAK",3) == 0)
+				else if (strncasecmp (word[4], "NAK", 3) == 0)
 				{
 					tcp_send_len (serv, "CAP END\r\n", 9);
 				}
+
 				return;
 		}
 	}
-#endif
 
 garbage:
 	/* unknown message */
diff --git a/src/common/servlist.c b/src/common/servlist.c
index 86a78ebf..e42688d9 100644
--- a/src/common/servlist.c
+++ b/src/common/servlist.c
@@ -608,13 +608,33 @@ servlist_connect (session *sess, ircnet *net, gboolean join)
 	}
 
 	serv->password[0] = 0;
+	serv->sasluser[0] = 0;
+	serv->saslpassword[0] = 0;
+
 	if (net->pass)
+	{
 		safe_strcpy (serv->password, net->pass, sizeof (serv->password));
+	}
+
+	if (net->flags & FLAG_USE_GLOBAL)
+	{
+		strcpy (serv->sasluser, prefs.hex_irc_user_name);
+	}
+	else
+	{
+		safe_strcpy (serv->sasluser, net->user, sizeof (serv->sasluser));
+	}
+
+	if (net->saslpass)
+	{
+		safe_strcpy (serv->saslpassword, net->saslpass, sizeof (serv->saslpassword));
+	}
 
 	if (net->flags & FLAG_USE_GLOBAL)
 	{
 		strcpy (serv->nick, prefs.hex_irc_nick1);
-	} else
+	}
+	else
 	{
 		if (net->nick)
 			strcpy (serv->nick, net->nick);
@@ -901,6 +921,7 @@ servlist_cleanup (void)
 	{
 		net = list->data;
 		free_and_clear (net->pass);
+		free_and_clear (net->saslpass);
 		free_and_clear (net->nickserv);
 	}
 }
@@ -923,6 +944,7 @@ servlist_net_remove (ircnet *net)
 	if (net->real)
 		free (net->real);
 	free_and_clear (net->pass);
+	free_and_clear (net->saslpass);
 	if (net->autojoin)
 		free (net->autojoin);
 	if (net->command)
@@ -1035,6 +1057,9 @@ servlist_load (void)
 			case 'P':
 				net->pass = strdup (buf + 2);
 				break;
+			case 'A':
+				net->saslpass = strdup (buf + 2);
+				break;
 			case 'J':
 				net->autojoin = strdup (buf + 2);
 				break;
@@ -1166,6 +1191,8 @@ servlist_save (void)
 			fprintf (fp, "R=%s\n", net->real);
 		if (net->pass)
 			fprintf (fp, "P=%s\n", net->pass);
+		if (net->saslpass)
+			fprintf (fp, "A=%s\n", net->saslpass);
 		if (net->autojoin)
 			fprintf (fp, "J=%s\n", net->autojoin);
 		if (net->nickserv)
diff --git a/src/common/servlist.h b/src/common/servlist.h
index e78f94b1..dc8095f4 100644
--- a/src/common/servlist.h
+++ b/src/common/servlist.h
@@ -14,6 +14,7 @@ typedef struct ircnet
 	char *user;
 	char *real;
 	char *pass;
+	char *saslpass;
 	char *autojoin;
 	char *command;
 	char *nickserv;
diff --git a/src/fe-gtk/servlistgui.c b/src/fe-gtk/servlistgui.c
index fe8aa319..40faaecc 100644
--- a/src/fe-gtk/servlistgui.c
+++ b/src/fe-gtk/servlistgui.c
@@ -73,6 +73,7 @@ static GtkWidget *edit_entry_user;
 static GtkWidget *edit_entry_real;
 static GtkWidget *edit_entry_join;
 static GtkWidget *edit_entry_pass;
+static GtkWidget *edit_entry_saslpass;
 static GtkWidget *edit_entry_cmd;
 static GtkWidget *edit_entry_nickserv;
 static GtkWidget *edit_label_nick;
@@ -490,6 +491,7 @@ servlist_edit_update (ircnet *net)
 	servlist_update_from_entry (&net->command, edit_entry_cmd);
 	servlist_update_from_entry (&net->nickserv, edit_entry_nickserv);
 	servlist_update_from_entry (&net->pass, edit_entry_pass);
+	servlist_update_from_entry (&net->saslpass, edit_entry_saslpass);
 }
 
 static void
@@ -1500,9 +1502,15 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
 					_("Password for the server, if in doubt, leave blank."));
 	gtk_entry_set_visibility (GTK_ENTRY (edit_entry_pass), FALSE);
 
+	edit_entry_saslpass =
+		servlist_create_entry (table3, _("SASL password:"), 19,
+									  net->saslpass, 0,
+					_("Password for SASL authentication, if in doubt, leave blank."));
+	gtk_entry_set_visibility (GTK_ENTRY (edit_entry_saslpass), FALSE);
+
 	label34 = gtk_label_new (_("Character set:"));
 	gtk_widget_show (label34);
-	gtk_table_attach (GTK_TABLE (table3), label34, 1, 2, 19, 20,
+	gtk_table_attach (GTK_TABLE (table3), label34, 1, 2, 20, 21,
 							(GtkAttachOptions) (GTK_FILL),
 							(GtkAttachOptions) (0), 0, 0);
 	gtk_misc_set_alignment (GTK_MISC (label34), 0, 0.5);
@@ -1512,7 +1520,7 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
 	gtk_entry_set_text (GTK_ENTRY (GTK_BIN (comboboxentry_charset)->child), net->encoding ? net->encoding : "System default");
 	ignore_changed = FALSE;
 	gtk_widget_show (comboboxentry_charset);
-	gtk_table_attach (GTK_TABLE (table3), comboboxentry_charset, 2, 3, 19, 20,
+	gtk_table_attach (GTK_TABLE (table3), comboboxentry_charset, 2, 3, 20, 21,
 							(GtkAttachOptions) (GTK_FILL),
 							(GtkAttachOptions) (GTK_FILL), 0, 0);