~sircmpwn/ChatSharp

bfddb78217ee191eac8874063e5e2bc89e1b17f9 — Alexandre Oliveira 4 years ago 64dbcdd
Add support for SASL PLAIN authentication
M ChatSharp/Handlers/CapabilityHandlers.cs => ChatSharp/Handlers/CapabilityHandlers.cs +9 -1
@@ 42,11 42,19 @@ namespace ChatSharp.Handlers
                    // Get the accepted capabilities
                    var acceptedCaps = message.Parameters[2].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string acceptedCap in acceptedCaps)
                    {
                        client.Capabilities.Enable(acceptedCap);
                        // Begin SASL authentication
                        if (acceptedCap == "sasl")
                        {
                            client.SendRawMessage("AUTHENTICATE PLAIN");
                            client.IsAuthenticatingSasl = true;
                        }
                    }

                    // Check if the enabled capabilities count is the same as the ones
                    // acknowledged by the server.
                    if (client.IsNegotiatingCapabilities && client.Capabilities.Enabled.Count() == acceptedCaps.Count())
                    if (client.IsNegotiatingCapabilities && client.Capabilities.Enabled.Count() == acceptedCaps.Count() && !client.IsAuthenticatingSasl)
                    {
                        client.SendRawMessage("CAP END");
                        client.IsNegotiatingCapabilities = false;

M ChatSharp/Handlers/MessageHandlers.cs => ChatSharp/Handlers/MessageHandlers.cs +9 -0
@@ 48,6 48,7 @@ namespace ChatSharp.Handlers
            client.SetHandler("330", UserHandlers.HandleWhoIsLoggedInAs);
            client.SetHandler("352", UserHandlers.HandleWho);
            client.SetHandler("354", UserHandlers.HandleWhox);
            client.SetHandler("900", UserHandlers.HandleLoggedIn); // ERR_LOGGEDIN
            client.SetHandler("ACCOUNT", UserHandlers.HandleAccount);
            client.SetHandler("CHGHOST", UserHandlers.HandleChangeHost);



@@ 76,6 77,14 @@ namespace ChatSharp.Handlers

            // Capability handlers
            client.SetHandler("CAP", CapabilityHandlers.HandleCapability);

            // SASL handlers
            client.SetHandler("AUTHENTICATE", SaslHandlers.HandleAuthentication);
            client.SetHandler("903", SaslHandlers.HandleError); // ERR_SASLSUCCESS
            client.SetHandler("904", SaslHandlers.HandleError); // ERR_SASLFAIL
            client.SetHandler("905", SaslHandlers.HandleError); // ERR_SASLTOOLONG
            client.SetHandler("906", SaslHandlers.HandleError); // ERR_SASLABORTED
            client.SetHandler("907", SaslHandlers.HandleError); // ERR_SASLALREADY
        }

        public static void HandleNick(IrcClient client, IrcMessage message)

A ChatSharp/Handlers/SaslHandlers.cs => ChatSharp/Handlers/SaslHandlers.cs +45 -0
@@ 0,0 1,45 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;

namespace ChatSharp.Handlers
{
    internal static class SaslHandlers
    {
        public static void HandleAuthentication(IrcClient client, IrcMessage message)
        {
            if (client.IsAuthenticatingSasl)
            {
                if (message.Parameters[0] == "+")
                {
                    // Based off irc-framework implementation
                    var plainString = string.Format("{0}\0{0}\0{1}", client.User.Nick, client.User.Password);
                    var b64Bytes = Encoding.UTF8.GetBytes(Convert.ToBase64String(Encoding.UTF8.GetBytes(plainString)));

                    while (b64Bytes.Length >= 400)
                    {
                        var chunk = b64Bytes.Take(400).ToArray();
                        b64Bytes = b64Bytes.Skip(400).ToArray();
                        client.SendRawMessage(string.Format("AUTHENTICATE {0}", Encoding.UTF8.GetString(chunk)));
                    }
                    if (b64Bytes.Length > 0)
                        client.SendRawMessage(string.Format("AUTHENTICATE {0}", Encoding.UTF8.GetString(b64Bytes)));
                    else
                        client.SendRawMessage("AUTHENTICATE +");

                    client.IsAuthenticatingSasl = false;
                }
            }
        }

        public static void HandleError(IrcClient client, IrcMessage message)
        {
            if (client.IsNegotiatingCapabilities && !client.IsAuthenticatingSasl)
            {
                client.SendRawMessage("CAP END");
                client.IsNegotiatingCapabilities = false;
            }
        }
    }
}

M ChatSharp/Handlers/UserHandlers.cs => ChatSharp/Handlers/UserHandlers.cs +5 -0
@@ 217,6 217,11 @@ namespace ChatSharp.Handlers
            }
        }

        public static void HandleLoggedIn(IrcClient client, IrcMessage message)
        {
            client.User.Account = message.Parameters[2];
        }

        public static void HandleWhoEnd(IrcClient client, IrcMessage message)
        {
            if (client.ServerInfo.ExtendedWho)

M ChatSharp/IrcClient.cs => ChatSharp/IrcClient.cs +7 -1
@@ 138,6 138,11 @@ namespace ChatSharp
        /// If set to False, capability negotiation is finished.
        /// </summary>
        public bool IsNegotiatingCapabilities { get; internal set; }
        /// <summary>
        /// Set to True when the client is authenticating with SASL.
        /// If set to False, SASL authentication is finished.
        /// </summary>
        public bool IsAuthenticatingSasl { get; internal set; }

        /// <summary>
        /// Creates a new IRC client, but will not connect until ConnectAsync is called.


@@ 169,10 174,11 @@ namespace ChatSharp
            // List of supported capabilities
            Capabilities.AddRange(new string[] {
                "server-time", "multi-prefix", "cap-notify", "znc.in/server-time", "znc.in/server-time-iso",
                "account-notify", "chghost", "userhost-in-names"
                "account-notify", "chghost", "userhost-in-names", "sasl"
            });

            IsNegotiatingCapabilities = false;
            IsAuthenticatingSasl = false;

            RandomNumber = new Random();
        }