Built-in SASL support and other CAP fixes

This commit is contained in:
Berke Viktor 2012-10-25 16:17:21 +02:00
parent 47310229a4
commit 4f4958878a
5 changed files with 89 additions and 10 deletions

View File

@ -485,6 +485,8 @@ typedef struct server
char hostname[128]; /* real ip number */ char hostname[128]; /* real ip number */
char servername[128]; /* what the server says is its name */ char servername[128]; /* what the server says is its name */
char password[86]; 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 nick[NICKLEN];
char linebuf[2048]; /* RFC says 512 chars including \r\n */ char linebuf[2048]; /* RFC says 512 chars including \r\n */
char *last_away_reason; char *last_away_reason;
@ -547,6 +549,7 @@ typedef struct server
unsigned int have_whox:1; /* have undernet's WHOX features */ unsigned int have_whox:1; /* have undernet's WHOX features */
unsigned int have_capab:1; /* supports CAPAB (005 tells us) */ unsigned int have_capab:1; /* supports CAPAB (005 tells us) */
unsigned int have_idmsg:1; /* freenode's IDENTIFY-MSG */ 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 have_except:1; /* ban exemptions +e */
unsigned int using_cp1255:1; /* encoding is CP1255/WINDOWS-1255? */ unsigned int using_cp1255:1; /* encoding is CP1255/WINDOWS-1255? */
unsigned int using_irc:1; /* encoding is "IRC" (CP1252/UTF-8 hybrid)? */ unsigned int using_irc:1; /* encoding is "IRC" (CP1252/UTF-8 hybrid)? */

View File

@ -28,6 +28,8 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#include <glib.h>
#include "hexchat.h" #include "hexchat.h"
#include "ctcp.h" #include "ctcp.h"
#include "fe.h" #include "fe.h"
@ -47,11 +49,12 @@
static void static void
irc_login (server *serv, char *user, char *realname) 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]) if (serv->password[0])
{
tcp_sendf (serv, "PASS %s\r\n", serv->password); 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, tcp_sendf (serv,
"NICK %s\r\n" "NICK %s\r\n"
@ -880,6 +883,15 @@ process_numeric (session * sess, int n,
notify_set_online (serv, word[4]); notify_set_online (serv, word[4]);
break; 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: default:
if (serv->inside_whois && word[4][0]) 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) else if (len == 3)
{ {
guint32 t; guint32 t;
int passlen;
char *encoded;
char *buffer;
t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]); t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]);
switch (t) 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) if (strncasecmp (word[5][0]==':' ? word[5] + 1 : word[5], "identify-msg", 12) == 0)
{ {
serv->have_idmsg = TRUE; 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) 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) if (strstr (word_eol[5], "identify-msg") != 0)
{ {
tcp_send_len (serv, "CAP REQ :identify-msg\r\n", 23); 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 else
{ {
/* if we use SASL, CAP END is dealt via raw numerics */
tcp_send_len (serv, "CAP END\r\n", 9); 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); tcp_send_len (serv, "CAP END\r\n", 9);
} }
return; return;
} }
} }
#endif
garbage: garbage:
/* unknown message */ /* unknown message */

View File

@ -608,13 +608,33 @@ servlist_connect (session *sess, ircnet *net, gboolean join)
} }
serv->password[0] = 0; serv->password[0] = 0;
serv->sasluser[0] = 0;
serv->saslpassword[0] = 0;
if (net->pass) if (net->pass)
{
safe_strcpy (serv->password, net->pass, sizeof (serv->password)); 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) if (net->flags & FLAG_USE_GLOBAL)
{ {
strcpy (serv->nick, prefs.hex_irc_nick1); strcpy (serv->nick, prefs.hex_irc_nick1);
} else }
else
{ {
if (net->nick) if (net->nick)
strcpy (serv->nick, net->nick); strcpy (serv->nick, net->nick);
@ -901,6 +921,7 @@ servlist_cleanup (void)
{ {
net = list->data; net = list->data;
free_and_clear (net->pass); free_and_clear (net->pass);
free_and_clear (net->saslpass);
free_and_clear (net->nickserv); free_and_clear (net->nickserv);
} }
} }
@ -923,6 +944,7 @@ servlist_net_remove (ircnet *net)
if (net->real) if (net->real)
free (net->real); free (net->real);
free_and_clear (net->pass); free_and_clear (net->pass);
free_and_clear (net->saslpass);
if (net->autojoin) if (net->autojoin)
free (net->autojoin); free (net->autojoin);
if (net->command) if (net->command)
@ -1035,6 +1057,9 @@ servlist_load (void)
case 'P': case 'P':
net->pass = strdup (buf + 2); net->pass = strdup (buf + 2);
break; break;
case 'A':
net->saslpass = strdup (buf + 2);
break;
case 'J': case 'J':
net->autojoin = strdup (buf + 2); net->autojoin = strdup (buf + 2);
break; break;
@ -1166,6 +1191,8 @@ servlist_save (void)
fprintf (fp, "R=%s\n", net->real); fprintf (fp, "R=%s\n", net->real);
if (net->pass) if (net->pass)
fprintf (fp, "P=%s\n", net->pass); fprintf (fp, "P=%s\n", net->pass);
if (net->saslpass)
fprintf (fp, "A=%s\n", net->saslpass);
if (net->autojoin) if (net->autojoin)
fprintf (fp, "J=%s\n", net->autojoin); fprintf (fp, "J=%s\n", net->autojoin);
if (net->nickserv) if (net->nickserv)

View File

@ -14,6 +14,7 @@ typedef struct ircnet
char *user; char *user;
char *real; char *real;
char *pass; char *pass;
char *saslpass;
char *autojoin; char *autojoin;
char *command; char *command;
char *nickserv; char *nickserv;

View File

@ -73,6 +73,7 @@ static GtkWidget *edit_entry_user;
static GtkWidget *edit_entry_real; static GtkWidget *edit_entry_real;
static GtkWidget *edit_entry_join; static GtkWidget *edit_entry_join;
static GtkWidget *edit_entry_pass; static GtkWidget *edit_entry_pass;
static GtkWidget *edit_entry_saslpass;
static GtkWidget *edit_entry_cmd; static GtkWidget *edit_entry_cmd;
static GtkWidget *edit_entry_nickserv; static GtkWidget *edit_entry_nickserv;
static GtkWidget *edit_label_nick; 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->command, edit_entry_cmd);
servlist_update_from_entry (&net->nickserv, edit_entry_nickserv); servlist_update_from_entry (&net->nickserv, edit_entry_nickserv);
servlist_update_from_entry (&net->pass, edit_entry_pass); servlist_update_from_entry (&net->pass, edit_entry_pass);
servlist_update_from_entry (&net->saslpass, edit_entry_saslpass);
} }
static void static void
@ -1500,9 +1502,15 @@ servlist_open_edit (GtkWidget *parent, ircnet *net)
_("Password for the server, if in doubt, leave blank.")); _("Password for the server, if in doubt, leave blank."));
gtk_entry_set_visibility (GTK_ENTRY (edit_entry_pass), FALSE); 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:")); label34 = gtk_label_new (_("Character set:"));
gtk_widget_show (label34); 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) (GTK_FILL),
(GtkAttachOptions) (0), 0, 0); (GtkAttachOptions) (0), 0, 0);
gtk_misc_set_alignment (GTK_MISC (label34), 0, 0.5); 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"); gtk_entry_set_text (GTK_ENTRY (GTK_BIN (comboboxentry_charset)->child), net->encoding ? net->encoding : "System default");
ignore_changed = FALSE; ignore_changed = FALSE;
gtk_widget_show (comboboxentry_charset); 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),
(GtkAttachOptions) (GTK_FILL), 0, 0); (GtkAttachOptions) (GTK_FILL), 0, 0);