mirror of
https://github.com/reactos/reactos.git
synced 2025-07-28 04:02:03 +00:00
Import TechBot
svn path=/trunk/; revision=13064
This commit is contained in:
parent
568b27baeb
commit
9dab4509fa
94 changed files with 24386 additions and 0 deletions
503
irc/TechBot/TechBot.IRCLibrary/IrcMessage.cs
Normal file
503
irc/TechBot/TechBot.IRCLibrary/IrcMessage.cs
Normal file
|
@ -0,0 +1,503 @@
|
|||
using System;
|
||||
|
||||
namespace TechBot.IRCLibrary
|
||||
{
|
||||
/*
|
||||
<message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
|
||||
<prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
|
||||
<command> ::= <letter> { <letter> } | <number> <number> <number>
|
||||
<SPACE> ::= ' ' { ' ' }
|
||||
<params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
|
||||
|
||||
<middle> ::= <Any *non-empty* sequence of octets not including SPACE
|
||||
or NUL or CR or LF, the first of which may not be ':'>
|
||||
<trailing> ::= <Any, possibly *empty*, sequence of octets not including
|
||||
NUL or CR or LF>
|
||||
|
||||
<crlf> ::= CR LF
|
||||
|
||||
NOTES:
|
||||
|
||||
1) <SPACE> is consists only of SPACE character(s) (0x20).
|
||||
Specially notice that TABULATION, and all other control
|
||||
characters are considered NON-WHITE-SPACE.
|
||||
|
||||
2) After extracting the parameter list, all parameters are equal,
|
||||
whether matched by <middle> or <trailing>. <Trailing> is just
|
||||
a syntactic trick to allow SPACE within parameter.
|
||||
|
||||
3) The fact that CR and LF cannot appear in parameter strings is
|
||||
just artifact of the message framing. This might change later.
|
||||
|
||||
4) The NUL character is not special in message framing, and
|
||||
basically could end up inside a parameter, but as it would
|
||||
cause extra complexities in normal C string handling. Therefore
|
||||
NUL is not allowed within messages.
|
||||
|
||||
5) The last parameter may be an empty string.
|
||||
|
||||
6) Use of the extended prefix (['!' <user> ] ['@' <host> ]) must
|
||||
not be used in server to server communications and is only
|
||||
intended for server to client messages in order to provide
|
||||
clients with more useful information about who a message is
|
||||
from without the need for additional queries.
|
||||
*/
|
||||
/*
|
||||
NOTICE AUTH :*** Looking up your hostname
|
||||
NOTICE AUTH :*** Checking Ident
|
||||
NOTICE AUTH :*** Found your hostname
|
||||
NOTICE AUTH :*** No ident response
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// IRC message.
|
||||
/// </summary>
|
||||
public class IrcMessage
|
||||
{
|
||||
#region Private fields
|
||||
private string line;
|
||||
private string prefix;
|
||||
private string prefixServername;
|
||||
private string prefixNickname;
|
||||
private string prefixUser;
|
||||
private string prefixHost;
|
||||
private string command;
|
||||
private string parameters;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Line of text that is to be parsed as an IRC message.
|
||||
/// </summary>
|
||||
public string Line
|
||||
{
|
||||
get
|
||||
{
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the message have a prefix?
|
||||
/// </summary>
|
||||
public bool HasPrefix
|
||||
{
|
||||
get
|
||||
{
|
||||
return prefix != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prefix or null if none exists.
|
||||
/// </summary>
|
||||
public string Prefix
|
||||
{
|
||||
get
|
||||
{
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Servername part of prefix or null if no prefix or servername exists.
|
||||
/// </summary>
|
||||
public string PrefixServername
|
||||
{
|
||||
get
|
||||
{
|
||||
return prefixServername;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Nickname part of prefix or null if no prefix or nick exists.
|
||||
/// </summary>
|
||||
public string PrefixNickname
|
||||
{
|
||||
get
|
||||
{
|
||||
return prefixNickname;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User part of (extended) prefix or null if no (extended) prefix exists.
|
||||
/// </summary>
|
||||
public string PrefixUser
|
||||
{
|
||||
get
|
||||
{
|
||||
return prefixUser;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Host part of (extended) prefix or null if no (extended) prefix exists.
|
||||
/// </summary>
|
||||
public string PrefixHost
|
||||
{
|
||||
get
|
||||
{
|
||||
return prefixHost;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command part of message.
|
||||
/// </summary>
|
||||
public string Command
|
||||
{
|
||||
get
|
||||
{
|
||||
return command;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is command numeric?
|
||||
/// </summary>
|
||||
public bool IsCommandNumeric
|
||||
{
|
||||
get
|
||||
{
|
||||
if (command == null || command.Length != 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
Int32.Parse(command);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command part of message as text.
|
||||
/// </summary>
|
||||
public string CommandText
|
||||
{
|
||||
get
|
||||
{
|
||||
return command;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Command part of message as a number.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown if IsCommandNumeric returns false.</exception>
|
||||
public int CommandNumber
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsCommandNumeric)
|
||||
{
|
||||
return Int32.Parse(command);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parameters part of message.
|
||||
/// </summary>
|
||||
public string Parameters
|
||||
{
|
||||
get
|
||||
{
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="line">Line of text that is to be parsed as an IRC message.</param>
|
||||
public IrcMessage(string line)
|
||||
{
|
||||
/*
|
||||
* <message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
|
||||
* <prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
|
||||
* :Oslo1.NO.EU.undernet.org 461 MYNICK USER :Not enough parameters
|
||||
*/
|
||||
try
|
||||
{
|
||||
this.line = line;
|
||||
int i = 0;
|
||||
|
||||
#region Prefix
|
||||
if (line[i].Equals(':'))
|
||||
{
|
||||
i++;
|
||||
prefix = "";
|
||||
/* This message has a prefix */
|
||||
string s = "";
|
||||
while (i < line.Length && line[i] != ' ' && line[i] != '!' && line[i] != '@')
|
||||
{
|
||||
s += line[i++];
|
||||
}
|
||||
if (IsValidIrcNickname(s))
|
||||
{
|
||||
prefixNickname = s;
|
||||
prefix += prefixNickname;
|
||||
if (line[i] == '!')
|
||||
{
|
||||
/* This message has an extended prefix */
|
||||
i++;
|
||||
s = "";
|
||||
while (i < line.Length && line[i] != ' ' && line[i] != '@')
|
||||
{
|
||||
s += line[i];
|
||||
i++;
|
||||
}
|
||||
prefixUser = s;
|
||||
prefix += "!" + prefixUser;
|
||||
}
|
||||
if (line[i] == '@')
|
||||
{
|
||||
/* This message has a host prefix */
|
||||
s = "";
|
||||
do
|
||||
{
|
||||
s += line[++i];
|
||||
}
|
||||
while (i < line.Length && line[i] != ' ');
|
||||
prefixHost = s;
|
||||
prefix += "@" + prefixHost;
|
||||
}
|
||||
}
|
||||
else /* Assume it is a servername */
|
||||
{
|
||||
prefixServername = s;
|
||||
prefix += prefixServername;
|
||||
}
|
||||
|
||||
/* Skip spaces */
|
||||
while (i < line.Length && line[i] == ' ')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prefix = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Command
|
||||
if (Char.IsDigit(line[i]))
|
||||
{
|
||||
if (!Char.IsDigit(line, i + 1) || !Char.IsDigit(line, i + 2))
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
command = String.Format("{0}{1}{2}", line[i++], line[i++], line[i++]);
|
||||
}
|
||||
else
|
||||
{
|
||||
command = "";
|
||||
while (i < line.Length && Char.IsLetter(line[i]))
|
||||
{
|
||||
command += line[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Parameters
|
||||
while (true)
|
||||
{
|
||||
/* Skip spaces */
|
||||
while (i < line.Length && line[i] == ' ')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
if (i < line.Length && line[i].Equals(':'))
|
||||
{
|
||||
i++;
|
||||
|
||||
/* Trailing */
|
||||
while (i < line.Length && line[i] != ' ' && line[i] != '\r' && line[i] != '\n' && line[i] != 0)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
parameters = "";
|
||||
}
|
||||
parameters += line[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Middle */
|
||||
while (i < line.Length && line[i] != '\r' && line[i] != '\n' && line[i] != 0)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
parameters = "";
|
||||
}
|
||||
parameters += line[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (i >= line.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new MalformedMessageException("The message is malformed.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="prefixServername"></param>
|
||||
/// <param name="prefixNickname"></param>
|
||||
/// <param name="prefixUser"></param>
|
||||
/// <param name="prefixHost"></param>
|
||||
/// <param name="command"></param>
|
||||
/// <param name="parameters"></param>
|
||||
public IrcMessage(string prefixServername,
|
||||
string prefixNickname,
|
||||
string prefixUser,
|
||||
string prefixHost,
|
||||
string command,
|
||||
string parameters)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="command">IRC command.</param>
|
||||
/// <param name="parameters">IRC command parameters. May be null if there are no parameters.</param>
|
||||
public IrcMessage(string command,
|
||||
string parameters)
|
||||
{
|
||||
if (command == null || !IsValidIrcCommand(command))
|
||||
{
|
||||
throw new ArgumentException("Command is not a valid IRC command.", "command");
|
||||
}
|
||||
/* An IRC message must not be longer than 512 characters (including terminating CRLF) */
|
||||
int parametersLength = (parameters != null) ? 1 + parameters.Length : 0;
|
||||
if (command.Length + parametersLength > 510)
|
||||
{
|
||||
throw new MalformedMessageException("IRC message cannot be longer than 512 characters.");
|
||||
}
|
||||
this.command = command;
|
||||
this.parameters = parameters;
|
||||
if (parameters != null)
|
||||
{
|
||||
this.line = String.Format("{0} {1}\r\n", command, parameters);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.line = String.Format("{0}\r\n", command);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns wether a string of text is a valid IRC command.
|
||||
/// </summary>
|
||||
/// <param name="command">The IRC command.</param>
|
||||
/// <returns>True, if <c ref="command">command</c> is a valid IRC command, false if not.</returns>
|
||||
private static bool IsValidIrcCommand(string command)
|
||||
{
|
||||
foreach (char c in command)
|
||||
{
|
||||
if (!Char.IsLetter(c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private const string IrcSpecial = @"-[]\`^{}";
|
||||
private const string IrcSpecialNonSpecs = @"_";
|
||||
|
||||
/// <summary>
|
||||
/// Returns wether a character is an IRC special character.
|
||||
/// </summary>
|
||||
/// <param name="c">Character to test.</param>
|
||||
/// <returns>True if the character is an IRC special character, false if not.</returns>
|
||||
private static bool IsSpecial(char c)
|
||||
{
|
||||
foreach (char validCharacter in IrcSpecial)
|
||||
{
|
||||
if (c.Equals(validCharacter))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
foreach (char validCharacter in IrcSpecialNonSpecs)
|
||||
{
|
||||
if (c.Equals(validCharacter))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns wether a string of text is a valid IRC nickname.
|
||||
/// </summary>
|
||||
/// <param name="nickname">The IRC nickname.</param>
|
||||
/// <returns>True, if <c ref="nickname">nickname</c> is a valid IRC nickname, false if not.</returns>
|
||||
private static bool IsValidIrcNickname(string nickname)
|
||||
{
|
||||
/*
|
||||
* <nick> ::= <letter> { <letter> | <number> | <special> }
|
||||
* <letter> ::= 'a' ... 'z' | 'A' ... 'Z'
|
||||
* <number> ::= '0' ... '9'
|
||||
* <special> ::= '-' | '[' | ']' | '\' | '`' | '^' | '{' | '}'
|
||||
*/
|
||||
/* An IRC nicknmame must be 1 - 9 characters in length. We don't care so much if it is larger */
|
||||
if ((nickname.Length < 1) || (nickname.Length > 30))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/* First character must be a letter. */
|
||||
if (!Char.IsLetter(nickname[0]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/* Check the other valid characters for validity. */
|
||||
foreach (char c in nickname)
|
||||
{
|
||||
if (!Char.IsLetter(c) && !Char.IsDigit(c) && !IsSpecial(c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write contents to a string.
|
||||
/// </summary>
|
||||
/// <returns>Contents as a string.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("Line({0})Prefix({1})Command({2})Parameters({3})",
|
||||
line, prefix != null ? prefix : "(null)",
|
||||
command != null ? command : "(null)",
|
||||
parameters != null ? parameters : "(null)");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue