Add basic capability support
The library now supports capability negotiation.
This commit is contained in:
parent
1e6f8e078f
commit
56e0c6ffeb
|
@ -0,0 +1,140 @@
|
|||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ChatSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// A list of capabilities supported by the library, along with the enabled and disabled
|
||||
/// capabilities after negotiating with the server.
|
||||
/// </summary>
|
||||
public class CapabilityPool : IEnumerable<IrcCapability>
|
||||
{
|
||||
private List<IrcCapability> Capabilities { get; set; }
|
||||
|
||||
internal CapabilityPool()
|
||||
{
|
||||
Capabilities = new List<IrcCapability>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the IrcCapability with the specified name.
|
||||
/// </summary>
|
||||
public IrcCapability this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
var cap = Capabilities.FirstOrDefault(c => c.Name == name);
|
||||
if (cap == null)
|
||||
throw new KeyNotFoundException();
|
||||
return cap;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Add(string name)
|
||||
{
|
||||
if (Capabilities.Any(cap => cap.Name == name))
|
||||
return;
|
||||
|
||||
Capabilities.Add(new IrcCapability(name));
|
||||
}
|
||||
|
||||
internal void AddRange(IEnumerable<string> range)
|
||||
{
|
||||
foreach (string item in range)
|
||||
Add(item);
|
||||
}
|
||||
|
||||
internal void Remove(string name)
|
||||
{
|
||||
Capabilities.Remove(this[name]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables the specified capability.
|
||||
/// </summary>
|
||||
internal void Enable(string name)
|
||||
{
|
||||
if (Capabilities.Any(cap => cap.Name == name && cap.IsEnabled))
|
||||
return;
|
||||
|
||||
this[name].IsEnabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables the specified capability.
|
||||
/// </summary>
|
||||
internal void Disable(string name)
|
||||
{
|
||||
if (Capabilities.Any(cap => cap.Name == name && !cap.IsEnabled))
|
||||
return;
|
||||
|
||||
this[name].IsEnabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified capability is enabled.
|
||||
/// </summary>
|
||||
internal bool IsEnabled(string name)
|
||||
{
|
||||
return Capabilities.Any(cap => cap.Name == name && cap.IsEnabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of enabled capabilities after negotiating capabilities with the server.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal IEnumerable<string> Enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return Capabilities.Where(cap => cap.IsEnabled).Select(x => x.Name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of disabled capabilities after negotiating capabilities with the server.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal IEnumerable<string> Disabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return Capabilities.Where(cap => cap.IsEnabled).Select(x => x.Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool Contains(string name)
|
||||
{
|
||||
return Capabilities.Any(cap => cap.Name == name);
|
||||
}
|
||||
|
||||
internal IrcCapability Get(string name)
|
||||
{
|
||||
if (Contains(name))
|
||||
return this[name];
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
|
||||
internal IrcCapability GetOrAdd(string name)
|
||||
{
|
||||
if (Contains(name))
|
||||
return this[name];
|
||||
|
||||
Add(name);
|
||||
return this[name];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates over the capabilities in this collection.
|
||||
/// </summary>
|
||||
public IEnumerator<IrcCapability> GetEnumerator()
|
||||
{
|
||||
return Capabilities.GetEnumerator();
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ChatSharp.Handlers
|
||||
{
|
||||
internal static class CapabilityHandlers
|
||||
{
|
||||
public static void HandleCapability(IrcClient client, IrcMessage message)
|
||||
{
|
||||
var serverCaps = new List<string>();
|
||||
var supportedCaps = client.Capabilities.ToArray();
|
||||
var requestedCaps = new List<string>();
|
||||
|
||||
switch (message.Parameters[1])
|
||||
{
|
||||
case "LS":
|
||||
// Parse server capabilities
|
||||
var serverCapsString = (message.Parameters[2] == "*" ? message.Parameters[3] : message.Parameters[2]);
|
||||
serverCaps.AddRange(serverCapsString.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries));
|
||||
|
||||
// CAP 3.2 multiline support. Send CAP requests on the last CAP LS line.
|
||||
// The last CAP LS line doesn't have * set as Parameters[2]
|
||||
if (message.Parameters[2] != "*")
|
||||
{
|
||||
// Check which capabilities we support that the server supports
|
||||
requestedCaps.AddRange(supportedCaps.Select(cap => cap.Name).Intersect(serverCaps));
|
||||
|
||||
// Check if we have to request any capability to be enabled.
|
||||
// If not, end the capability negotiation.
|
||||
if (requestedCaps.Count > 0)
|
||||
client.SendRawMessage("CAP REQ :{0}", string.Join(" ", requestedCaps));
|
||||
else
|
||||
client.SendRawMessage("CAP END");
|
||||
}
|
||||
break;
|
||||
case "ACK":
|
||||
// Get the accepted capabilities
|
||||
var acceptedCaps = message.Parameters[2].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (string acceptedCap in acceptedCaps)
|
||||
client.Capabilities.Enable(acceptedCap);
|
||||
|
||||
// Check if the enabled capabilities count is the same as the ones
|
||||
// acknowledged by the server.
|
||||
if (client.Capabilities.Enabled.Count() == acceptedCaps.Count())
|
||||
client.SendRawMessage("CAP END");
|
||||
|
||||
break;
|
||||
case "NAK":
|
||||
// Get the rejected capabilities
|
||||
var rejectedCaps = message.Parameters[2].Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (string acceptedCap in rejectedCaps)
|
||||
client.Capabilities.Disable(acceptedCap);
|
||||
|
||||
// Check if the disabled capabilities count is the same as the ones
|
||||
// rejected by the server.
|
||||
if (client.Capabilities.Disabled.Count() == rejectedCaps.Count())
|
||||
client.SendRawMessage("CAP END");
|
||||
|
||||
break;
|
||||
case "LIST":
|
||||
// Not implemented yet
|
||||
break;
|
||||
case "NEW":
|
||||
// Not implemented yet
|
||||
break;
|
||||
case "DEL":
|
||||
// Not implemented yet
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -67,6 +67,9 @@ namespace ChatSharp.Handlers
|
|||
client.SetHandler("405", ErrorHandlers.HandleError);//ERR_TOOMANYCHANNELS "<channel name> :You have joined too many \ channels"
|
||||
client.SetHandler("406", ErrorHandlers.HandleError);//ERR_WASNOSUCHNICK "<nickname> :There was no such nickname"
|
||||
client.SetHandler("407", ErrorHandlers.HandleError);//ERR_TOOMANYTARGETS "<target> :Duplicate recipients. No message \
|
||||
|
||||
// Capability handlers
|
||||
client.SetHandler("CAP", CapabilityHandlers.HandleCapability);
|
||||
}
|
||||
|
||||
public static void HandleNick(IrcClient client, IrcMessage message)
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ChatSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// A IRC capability.
|
||||
/// </summary>
|
||||
public class IrcCapability
|
||||
{
|
||||
/// <summary>
|
||||
/// The capability identifier.
|
||||
/// </summary>
|
||||
public string Name { get; internal set; }
|
||||
/// <summary>
|
||||
/// The state of the capability after negotiating with the server.
|
||||
/// Disabled by default when the capability is created.
|
||||
/// </summary>
|
||||
public bool IsEnabled { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a capability given a name.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
public IrcCapability(string name)
|
||||
{
|
||||
Name = name;
|
||||
IsEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -126,6 +126,11 @@ namespace ChatSharp
|
|||
/// A list of users on this network that we are aware of.
|
||||
/// </summary>
|
||||
public UserPool Users { get; set; }
|
||||
/// <summary>
|
||||
/// A list of capabilities supported by the library, along with enabled and disabled capabilities
|
||||
/// after negotiating with the server.
|
||||
/// </summary>
|
||||
public CapabilityPool Capabilities { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new IRC client, but will not connect until ConnectAsync is called.
|
||||
|
@ -152,6 +157,8 @@ namespace ChatSharp
|
|||
PrivmsgPrefix = "";
|
||||
Users = new UserPool();
|
||||
Users.Add(User); // Add self to user pool
|
||||
Capabilities = new CapabilityPool();
|
||||
Capabilities.AddRange(new string[] { "server-time" }); // List of supported capabilities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -225,6 +232,8 @@ namespace ChatSharp
|
|||
}
|
||||
|
||||
NetworkStream.BeginRead(ReadBuffer, ReadBufferIndex, ReadBuffer.Length, DataRecieved, null);
|
||||
// Begin capability negotiation
|
||||
SendRawMessage("CAP LS 302");
|
||||
// Write login info
|
||||
if (!string.IsNullOrEmpty(User.Password))
|
||||
SendRawMessage("PASS {0}", User.Password);
|
||||
|
|
Loading…
Reference in New Issue