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 + 4 files changed, 79 insertions(+), 8 deletions(-) (limited to 'src/common') 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; -- cgit 1.4.1