summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/hexchat.h3
-rw-r--r--src/common/plugin.c2
-rw-r--r--src/common/proto-irc.c45
-rw-r--r--src/common/servlist.c29
-rw-r--r--src/common/servlist.h1
-rw-r--r--src/common/util.c24
-rw-r--r--src/common/util.h1
-rw-r--r--src/fe-gtk/servlistgui.c12
-rw-r--r--src/fe-gtk/setup.c8
9 files changed, 108 insertions, 17 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/plugin.c b/src/common/plugin.c
index cc626659..ac1bce45 100644
--- a/src/common/plugin.c
+++ b/src/common/plugin.c
@@ -479,7 +479,7 @@ plugin_auto_load (session *sess)
 	for_files ("./plugins", "hcmpcinfo.dll", plugin_auto_load_cb);
 	for_files ("./plugins", "hcperl.dll", plugin_auto_load_cb);
 	for_files ("./plugins", "hcpython.dll", plugin_auto_load_cb);
-	for_files ("./plugins", "hcsasl.dll", plugin_auto_load_cb);
+	/* for_files ("./plugins", "hcsasl.dll", plugin_auto_load_cb); we have this built-in */
 	for_files ("./plugins", "hctcl.dll", plugin_auto_load_cb);
 	for_files ("./plugins", "hcupd.dll", plugin_auto_load_cb);
 	for_files ("./plugins", "hcwinamp.dll", plugin_auto_load_cb);
diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c
index 342ad221..b8899855 100644
--- a/src/common/proto-irc.c
+++ b/src/common/proto-irc.c
@@ -47,11 +47,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 +881,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 +1127,10 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 		}
 	}
 
-#if 0	/* breaks the SASL plugin */
 	else if (len == 3)
 	{
 		guint32 t;
+		char *pass;
 
 		t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]);
 		switch (t)
@@ -1131,28 +1141,45 @@ 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);
+
+						pass = encode_sasl_pass (sess->server->sasluser, sess->server->saslpassword);
+						tcp_sendf (sess->server, "AUTHENTICATE %s\r\n", pass);
+						free (pass);
 					}
 				}
 				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 */
@@ -1184,6 +1211,10 @@ process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol
 		EMIT_SIGNAL (XP_TE_SERVNOTICE, sess, buf, sess->server->servername, NULL, NULL, 0);
 		return;
 	}
+	if (!strncmp (buf, "AUTHENTICATE +", 14))	/* omit SASL "empty" responses */
+	{
+		return;
+	}
 
 	EMIT_SIGNAL (XP_TE_SERVTEXT, sess, buf, sess->server->servername, rawname, NULL, 0);
 }
diff --git a/src/common/servlist.c b/src/common/servlist.c
index 86a78ebf..6529b162 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 if (net->user != NULL)
+	{
+		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/common/util.c b/src/common/util.c
index 5120c25a..0881c708 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -681,7 +681,7 @@ get_cpu_str (void)
 	double mhz;
 
 	osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX);
-	GetVersionEx (&osvi);
+	GetVersionEx ((OSVERSIONINFO*) &osvi);
 
 	switch (osvi.dwMajorVersion)
 	{
@@ -740,7 +740,7 @@ get_cpu_str (void)
 					}
 					else
 					{
-						strcpy (winver, "8 Server");
+						strcpy (winver, "Server 2012");
 					}
 					break;
 			}
@@ -1966,3 +1966,23 @@ get_subdirs (const char *path)
 
 	return dirlist;
 }
+
+char *
+encode_sasl_pass (char *user, char *pass)
+{
+	int passlen;
+	char *buffer;
+	char *encoded;
+
+	/* passphrase generation, nicely copy-pasted from the CAP-SASL plugin */
+	passlen = strlen (user) * 2 + 2 + strlen (pass);
+	buffer = (char*) malloc (passlen + 1);
+	strcpy (buffer, user);
+	strcpy (buffer + strlen (user) + 1, user);
+	strcpy (buffer + strlen (user) * 2 + 2, pass);
+	encoded = g_base64_encode ((unsigned char*) buffer, passlen);
+
+	free (buffer);
+
+	return encoded;
+}
diff --git a/src/common/util.h b/src/common/util.h
index 484cd617..d2e8e84e 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -61,5 +61,6 @@ void canonalize_key (char *key);
 int portable_mode ();
 int hextray_mode ();
 GSList *get_subdirs (const char *path);
+char *encode_sasl_pass (char *user, char *pass);
 
 #endif
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);
 
diff --git a/src/fe-gtk/setup.c b/src/fe-gtk/setup.c
index a83e423a..15c8dcc0 100644
--- a/src/fe-gtk/setup.c
+++ b/src/fe-gtk/setup.c
@@ -168,10 +168,6 @@ static const setting appearance_settings[] =
 	{ST_EFONT,  N_("Font:"), P_OFFSETNL(hex_text_font), 0, 0, sizeof prefs.hex_text_font},
 #endif
 
-	{ST_HEADER,	N_("Title Bar"),0,0,0},
-	{ST_TOGGLE, N_("Show channel modes"), P_OFFINTNL(hex_gui_win_modes),0,0,0},
-	{ST_TOGGLR, N_("Show number of users"), P_OFFINTNL(hex_gui_win_ucount),0,0,0},
-
 	{ST_HEADER,	N_("Text Box"),0,0,0},
 	{ST_EFILE,  N_("Background image:"), P_OFFSETNL(hex_text_background), 0, 0, sizeof prefs.hex_text_background},
 	{ST_TOGGLE, N_("Colored nick names"), P_OFFINTNL(hex_text_color_nicks),
@@ -198,6 +194,10 @@ static const setting appearance_settings[] =
 					N_("See the strftime manpage for details."),0,sizeof prefs.hex_stamp_text_format},
 #endif
 
+	{ST_HEADER,	N_("Title Bar"),0,0,0},
+	{ST_TOGGLE, N_("Show channel modes"), P_OFFINTNL(hex_gui_win_modes),0,0,0},
+	{ST_TOGGLR, N_("Show number of users"), P_OFFINTNL(hex_gui_win_ucount),0,0,0},
+
 	{ST_END, 0, 0, 0, 0, 0}
 };