From 4f4958878a74d97f9f43e8add855bc2ee22c1de4 Mon Sep 17 00:00:00 2001 From: Berke Viktor Date: Thu, 25 Oct 2012 16:17:21 +0200 Subject: Built-in SASL support and other CAP fixes --- src/common/hexchat.h | 3 +++ src/common/proto-irc.c | 54 +++++++++++++++++++++++++++++++++++++++++------- src/common/servlist.c | 29 +++++++++++++++++++++++++- src/common/servlist.h | 1 + src/fe-gtk/servlistgui.c | 12 +++++++++-- 5 files changed, 89 insertions(+), 10 deletions(-) (limited to 'src') 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 #endif +#include + #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); -- cgit 1.4.1