fteqw/plugins/irc/ircclient.c

1231 lines
35 KiB
C
Raw Normal View History

//Released under the terms of the gpl as this file uses a bit of quake derived code. All sections of the like are marked as such
#include "../plugin.h"
#define Q_strncpyz(o, i, l) do {strncpy(o, i, l-1);o[l-1]='\0';}while(0)
//porting zone:
#define Q_strncpyz(o, i, l) do {strncpy(o, i, l-1);o[l-1]='\0';}while(0)
#include <winsock.h>
#define printf Con_Printf
#define dprintf Con_Printf
#define wprintf Con_Printf
#define twprintf Con_Printf
#define COLOURGREEN "^2"
#define COMMANDPREFIX "irc "
#define playsound(s)
#define IRC_Malloc malloc
#define IRC_Free free
#define TL_NETGETPACKETERROR "NET_GetPacket Error %s\n"
#define TOKENSIZE 1024
char com_token[TOKENSIZE];
char *COM_Parse (char *data) //this is taken out of quake
{
int c;
int len;
len = 0;
com_token[0] = 0;
if (!data)
return NULL;
// skip whitespace
skipwhite:
while ( (c = *data) <= ' ')
{
if (c == 0)
return NULL; // end of file;
data++;
}
// skip // comments
if (c=='/')
{
if (data[1] == '/')
{
while (*data && *data != '\n')
data++;
goto skipwhite;
}
}
// handle quoted strings specially
if (c == '\"')
{
data++;
while (1)
{
if (len >= TOKENSIZE-1)
return data;
c = *data++;
if (c=='\"' || !c)
{
com_token[len] = 0;
return data;
}
com_token[len] = c;
len++;
}
}
// parse a regular word
do
{
if (len >= TOKENSIZE-1)
return data;
com_token[len] = c;
data++;
len++;
c = *data;
} while (c>32);
com_token[len] = 0;
return data;
}
//functions are geared towards windows because windows is fussiest.
//constants towards unix cos they are more proper
#ifdef _WIN32
#include <winsock.h>
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EMSGSIZE WSAEMSGSIZE
#define ECONNRESET WSAECONNRESET
#define ECONNABORTED WSAECONNABORTED
#define ECONNREFUSED WSAECONNREFUSED
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
#define qerrno WSAGetLastError()
#else
#define qerrno errno
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#ifdef sun
#include <sys/filio.h>
#endif
#ifdef NeXT
#include <libc.h>
#endif
#define closesocket close
#define ioctlsocket ioctl
#endif
void IRC_Command(void);
int IRC_ExecuteCommand(int *args)
{
char cmd[8];
Cmd_Argv(0, cmd, sizeof(cmd));
if (!strcmp(cmd, "irc"))
{
IRC_Command();
return true;
}
return false;
}
int IRC_Frame(int *args);
int Plug_Init(int *args)
{
if ( Plug_Export("Tick", IRC_Frame) &&
Plug_Export("ExecuteCommand", IRC_ExecuteCommand))
Con_Print("IRC Client Plugin Loaded\n");
else
Con_Print("IRC Client Plugin failed\n");
return 0;
}
int IRC_StringToSockaddr (char *s, struct sockaddr *sadr) //From Quake2
{
struct hostent *h;
char *colon;
char copy[128];
memset (sadr, 0, sizeof(*sadr));
((struct sockaddr_in *)sadr)->sin_family = AF_INET;
((struct sockaddr_in *)sadr)->sin_port = 0;
strcpy (copy, s);
// strip off a trailing :port if present
for (colon = copy ; *colon ; colon++)
if (*colon == ':')
{
*colon = 0;
((struct sockaddr_in *)sadr)->sin_port = htons((short)atoi(colon+1));
}
if (copy[0] >= '0' && copy[0] <= '9')
{
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy);
}
else
{
if (! (h = gethostbyname(copy)) )
return 0;
*(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0];
}
return true;
}
//\r\n is used to end a line.
//meaning \0s are valid.
//but never used cos it breaks strings
#define IRC_MAXNICKLEN 32 //9 and a null term
#define IRC_MAXMSGLEN 512
char defaultnick[IRC_MAXNICKLEN+1];
typedef struct {
char server[64];
int port;
int socket;
char nick[IRC_MAXNICKLEN];
char pwd[64];
char realname[128];
char hostname[128];
char defaultdest[IRC_MAXNICKLEN];//channel or nick
char bufferedinmessage[IRC_MAXMSGLEN+1]; //there is a max size for protocol. (conveinient eh?) (and it's text format)
int bufferedinammount;
} ircclient_t;
ircclient_t *ircclient;
void IRC_AddClientMessage(ircclient_t *irc, char *msg)
{
send(irc->socket, msg, strlen(msg), 0); //FIXME: This needs rewriting to cope with errors.
}
ircclient_t *IRC_Connect(char *server, int port)
{
ircclient_t *irc;
unsigned long _true = true;
struct sockaddr_in from;
irc = IRC_Malloc(sizeof(ircclient_t));
if (!irc)
return NULL;
memset(irc, 0, sizeof(ircclient_t));
if ((irc->socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
{
wprintf ("IRC_TCP_OpenSocket: socket: %s\n", strerror(qerrno));
return NULL;
}
IRC_StringToSockaddr(server, (struct sockaddr *)&from);
//not yet blocking. So no frequent attempts please...
//non blocking prevents connect from returning worthwhile sensible value.
if (connect(irc->socket, (struct sockaddr *)&from, sizeof(from)) == -1)
{
wprintf ("IRC_TCP_OpenSocket: connect: %i %s\n", qerrno, strerror(qerrno));
closesocket(irc->socket);
IRC_Free(irc);
return NULL;
}
if (ioctlsocket (irc->socket, FIONBIO, &_true) == -1) //now make it non blocking.
{
wprintf ("IRC_TCP_OpenSocket: ioctl FIONBIO: %s\n", strerror(qerrno));
return NULL;
}
Q_strncpyz(irc->server, server, sizeof(irc->server));
strcpy(irc->nick, "anonymous");
strcpy(irc->realname, "anonymous");
gethostname(irc->hostname, sizeof(irc->hostname));
irc->hostname[sizeof(irc->hostname)-1] = 0;
return irc;
}
void IRC_SetPass(ircclient_t *irc, char *pass)
{
IRC_AddClientMessage(irc, va("PASS %s\r\n", pass));
}
void IRC_SetNick(ircclient_t *irc, char *nick)
{
IRC_AddClientMessage(irc, va("NICK %s\r\n", nick));
}
void IRC_SetUser(ircclient_t *irc, char *user)
{
IRC_AddClientMessage(irc, va("USER %s %s %s :%s\r\n", user, irc->hostname, irc->server, "Something"));
}
#define IRC_DONE 0
#define IRC_CONTINUE 1
#define IRC_KILL 2
int IRC_ClientFrame(ircclient_t *irc)
{
char prefix[64];
int ret;
char *nextmsg, *msg;
ret = recv(irc->socket, irc->bufferedinmessage+irc->bufferedinammount, sizeof(irc->bufferedinmessage)-1 - irc->bufferedinammount, 0);
if (ret == 0)
return IRC_KILL;
if (ret == -1)
{
if (qerrno == EWOULDBLOCK)
{
if (!irc->bufferedinammount) //if we are half way through a message, read any possible conjunctions.
return IRC_DONE; //remove
}
else
{
if (qerrno == ECONNABORTED || qerrno == ECONNRESET)
return IRC_KILL;
twprintf (TL_NETGETPACKETERROR, strerror(qerrno));
return IRC_KILL;
}
}
if (ret>0)
irc->bufferedinammount+=ret;
irc->bufferedinmessage[irc->bufferedinammount] = '\0';
nextmsg = strstr(irc->bufferedinmessage, "\r\n");
if (!nextmsg)
return IRC_DONE;
*nextmsg = '\0';
nextmsg+=2;
msg = irc->bufferedinmessage;
if (*msg == ':') //we need to strip off the prefix
{
char *sp = strchr(msg, ' ');
if (!sp)
{
dprintf("Ignoring bad message\n%s\n", msg);
memmove(irc->bufferedinmessage, nextmsg, irc->bufferedinammount - (msg-irc->bufferedinmessage));
irc->bufferedinammount-=nextmsg-irc->bufferedinmessage;
return IRC_CONTINUE;
}
if (sp-msg >= sizeof(prefix))
Q_strncpyz(prefix, msg+1, sizeof(prefix));
else
Q_strncpyz(prefix, msg+1, sp-msg);
msg = sp;
while(*msg == ' ')
msg++;
}
else
strcpy(prefix, irc->server);
if (!strncmp(msg, "PING ", 5))
{
dprintf("%s\n", msg);
IRC_AddClientMessage(irc, va("PONG %s\r\n", msg+5));
}
else if (!strncmp(msg, "PRIVMSG ", 7) || !strncmp(msg, "NOTICE ", 6)) //no autoresponses to notice please, and any autoresponses should be in the form of a notice
{
char *exc = strchr(prefix, '!');
char *col = strchr(msg+6, ':');
char *end;
playsound ("misc/talk.wav");
if (exc && col)
{
*exc = '\0';
if (!strncmp(col, ":\001", 2))
{
end = strchr(col+2, '\001');
if (end)
*end = '\0';
if (!strncmp(col+2, "ACTION ", 7))
printf(COLOURGREEN "***%s %s\n", prefix, col+9); //from client
else
{
if (end)//put it back on. might as well.
*end = '\001';
printf(COLOURGREEN "%s: %s\n", prefix, col+1); //from client
}
}
else
printf(COLOURGREEN "%s: %s\n", prefix, col+1); //from client
}
else dprintf(COLOURGREEN ":%s%s\n", prefix, msg); //direct server message
}
else if (!strncmp(msg, "NICK ", 5))
{
char *exc = strchr(prefix, '!');
char *col = strchr(msg+6, ':');
if (exc && col)
{
*exc = '\0';
printf(COLOURGREEN "%s changes name to %s\n", prefix, col+1);
}
else dprintf(COLOURGREEN ":%s%s\n", prefix, msg+6);
}
else if (!strncmp(msg, "PART ", 5))
{
char *exc = strchr(prefix, '!');
char *col = strchr(msg+5, ':');
if (!col)
col = msg+5;
else col+=1;
if (exc)
{
*exc = '\0';
printf(COLOURGREEN "%s leaves channel %s\n", prefix, col);
}
else dprintf(COLOURGREEN ":%sPART %s\n", prefix, msg+5);
}
else if (!strncmp(msg, "JOIN ", 5))
{
char *exc = strchr(prefix, '!');
char *col = strchr(msg+5, ':');
if (exc && col)
{
*exc = '\0';
printf(COLOURGREEN "%s joins channel %s\n", prefix, col+1);
}
else dprintf(COLOURGREEN ":%sJOIN %s\n", prefix, msg+5);
}
else if (!strncmp(msg, "322 ", 4)) //channel listing
{
dprintf("%s\n", msg);
}
else if (!strncmp(msg, "375 ", 4))
{
dprintf("%s\n", msg);
}
else if (!strncmp(msg, "431 ", 4))
{
dprintf("You need to specify a nickname\n");
}
else if (!strncmp(msg, "432 ", 4))
{
dprintf("Erroneous/invalid nickname given\n");
}
else if (!strncmp(msg, "433 ", 4))
{
dprintf("Nickname is already in use by someone\n");
}
else if (!strncmp(msg, "436 ", 4))
{
dprintf("Nickname collision. You will probably need to reconnect\n");
}
else if (!strncmp(msg, "372 ", 4))
{
char *text = strstr(msg, ":-");
if (text)
dprintf("%s\n", text+2);
else
dprintf("%s\n", msg);
}
/*
else if (!strncmp(msg, "401 ", 4))
{
dprintf("Nickname/channel does not exist\n", msg);
}
else if (!strncmp(msg, "402 ", 4))
{
dprintf("No such server\n", msg);
}
else if (!strncmp(msg, "403 ", 4))
{
dprintf("No such channel\n", msg);
}
else if (!strncmp(msg, "404 ", 4))
{
dprintf("Cannot send to that channel\n", msg);
}
else if (!strncmp(msg, "405 ", 4))
{
dprintf("You may not join annother channel\n", msg);
}
else if (!strncmp(msg, "406 ", 4))
{
dprintf("Nickname does not exist\n", msg);
}
else if (!strncmp(msg, "407 ", 4))
{
dprintf("Too many targets. Try to specify a specific nickname.\n", msg);
}
else if (!strncmp(msg, "409 ", 4))
{
dprintf("No origin specified\n", msg);
}
else if (!strncmp(msg, "411 ", 4))
{
dprintf("No recipient given.\n", msg);
}
else if (!strncmp(msg, "412 ", 4))
{
dprintf("No text given\n", msg);
}
else if (!strncmp(msg, "413 ", 4))
{
dprintf("No top level domain specified\n", msg);
}
else if (!strncmp(msg, "414 ", 4))
{
dprintf("Wildcard in toplevel domain\n", msg);
}
else if (!strncmp(msg, "421 ", 4))
{
dprintf("Unknown command.\n", msg);
}
else if (!strncmp(msg, "422 ", 4))
{
dprintf("MOTD file is missing (awww)\n", msg);
}
else if (!strncmp(msg, "423 ", 4))
{
dprintf("No administrative info is available\n", msg);
}
else if (!strncmp(msg, "424 ", 4))
{
dprintf("Generic file error\n", msg);
}
else if (!strncmp(msg, "441 ", 4))
{
dprintf("User isn't in that channel\n", msg);
}
else if (!strncmp(msg, "442 ", 4))
{
dprintf("You are not on that channel\n", msg);
}
else if (!strncmp(msg, "443 ", 4))
{
dprintf("The user is already on that channel\n", msg);
}
else if (!strncmp(msg, "444 ", 4))
{
dprintf("User not logged in\n", msg);
}
else if (!strncmp(msg, "445 ", 4))
{
dprintf("SUMMON has been disabled\n", msg);
}
else if (!strncmp(msg, "446 ", 4))
{
dprintf("USERS has been disabled\n", msg);
}
else if (!strncmp(msg, "451 ", 4))
{
dprintf("You have not registered\n", msg);
}
461 ERR_NEEDMOREPARAMS
"<command> :Not enough parameters"
- Returned by the server by numerous commands to
indicate to the client that it didn't supply enough
parameters.
462 ERR_ALREADYREGISTRED
":You may not reregister"
- Returned by the server to any link which tries to
change part of the registered details (such as
password or user details from second USER message).
463 ERR_NOPERMFORHOST
":Your host isn't among the privileged"
- Returned to a client which attempts to register with
a server which does not been setup to allow
connections from the host the attempted connection
is tried.
--------------------------------------------------------------------------------
Page 47
464 ERR_PASSWDMISMATCH
":Password incorrect"
- Returned to indicate a failed attempt at registering
a connection for which a password was required and
was either not given or incorrect.
465 ERR_YOUREBANNEDCREEP
":You are banned from this server"
- Returned after an attempt to connect and register
yourself with a server which has been setup to
explicitly deny connections to you.
467 ERR_KEYSET
"<channel> :Channel key already set"
471 ERR_CHANNELISFULL
"<channel> :Cannot join channel (+l)"
472 ERR_UNKNOWNMODE
"<char> :is unknown mode char to me"
473 ERR_INVITEONLYCHAN
"<channel> :Cannot join channel (+i)"
474 ERR_BANNEDFROMCHAN
"<channel> :Cannot join channel (+b)"
475 ERR_BADCHANNELKEY
"<channel> :Cannot join channel (+k)"
481 ERR_NOPRIVILEGES
":Permission Denied- You're not an IRC operator"
- Any command requiring operator privileges to operate
must return this error to indicate the attempt was
unsuccessful.
482 ERR_CHANOPRIVSNEEDED
"<channel> :You're not channel operator"
- Any command requiring 'chanop' privileges (such as
MODE messages) must return this error if the client
making the attempt is not a chanop on the specified
channel.
483 ERR_CANTKILLSERVER
":You cant kill a server!"
- Any attempts to use the KILL command on a server
are to be refused and this error returned directly
to the client.
--------------------------------------------------------------------------------
Page 48
491 ERR_NOOPERHOST
":No O-lines for your host"
- If a client sends an OPER message and the server has
not been configured to allow connections from the
client's host as an operator, this error must be
returned.
501 ERR_UMODEUNKNOWNFLAG
":Unknown MODE flag"
- Returned by the server to indicate that a MODE
message was sent with a nickname parameter and that
the a mode flag sent was not recognized.
502 ERR_USERSDONTMATCH
":Cant change mode for other users"
- Error sent to any user trying to view or change the
user mode for a user other than themselves.
6.2 Command responses.
300 RPL_NONE
Dummy reply number. Not used.
302 RPL_USERHOST
":[<reply>{<space><reply>}]"
- Reply format used by USERHOST to list replies to
the query list. The reply string is composed as
follows:
<reply> ::= <nick>['*'] '=' <'+'|'-'><hostname>
The '*' indicates whether the client has registered as an Operator. The '-' or '+' characters represent whether the client has set an AWAY message or not respectively.
303 RPL_ISON
":[<nick> {<space><nick>}]"
- Reply format used by ISON to list replies to the
query list.
301 RPL_AWAY
"<nick> :<away message>"
--------------------------------------------------------------------------------
Page 49
305 RPL_UNAWAY
":You are no longer marked as being away"
306 RPL_NOWAWAY
":You have been marked as being away"
- These replies are used with the AWAY command (if
allowed). RPL_AWAY is sent to any client sending a
PRIVMSG to a client which is away. RPL_AWAY is only
sent by the server to which the client is connected.
Replies RPL_UNAWAY and RPL_NOWAWAY are sent when the
client removes and sets an AWAY message.
311 RPL_WHOISUSER
"<nick> <user> <host> * :<real name>"
312 RPL_WHOISSERVER
"<nick> <server> :<server info>"
313 RPL_WHOISOPERATOR
"<nick> :is an IRC operator"
317 RPL_WHOISIDLE
"<nick> <integer> :seconds idle"
318 RPL_ENDOFWHOIS
"<nick> :End of /WHOIS list"
319 RPL_WHOISCHANNELS
"<nick> :{[@|+]<channel><space>}"
- Replies 311 - 313, 317 - 319 are all replies
generated in response to a WHOIS message. Given that
there are enough parameters present, the answering
server must either formulate a reply out of the above
numerics (if the query nick is found) or return an
error reply. The '*' in RPL_WHOISUSER is there as
the literal character and not as a wild card. For
each reply set, only RPL_WHOISCHANNELS may appear
more than once (for long lists of channel names).
The '@' and '+' characters next to the channel name
indicate whether a client is a channel operator or
has been granted permission to speak on a moderated
channel. The RPL_ENDOFWHOIS reply is used to mark
the end of processing a WHOIS message.
314 RPL_WHOWASUSER
"<nick> <user> <host> * :<real name>"
369 RPL_ENDOFWHOWAS
"<nick> :End of WHOWAS"
- When replying to a WHOWAS message, a server must use
the replies RPL_WHOWASUSER, RPL_WHOISSERVER or
ERR_WASNOSUCHNICK for each nickname in the presented
--------------------------------------------------------------------------------
Page 50
list. At the end of all reply batches, there must be RPL_ENDOFWHOWAS (even if there was only one reply and it was an error).
321 RPL_LISTSTART
"Channel :Users Name"
322 RPL_LIST
"<channel> <# visible> :<topic>"
323 RPL_LISTEND
":End of /LIST"
- Replies RPL_LISTSTART, RPL_LIST, RPL_LISTEND mark
the start, actual replies with data and end of the
server's response to a LIST command. If there are
no channels available to return, only the start
and end reply must be sent.
324 RPL_CHANNELMODEIS
"<channel> <mode> <mode params>"
331 RPL_NOTOPIC
"<channel> :No topic is set"
332 RPL_TOPIC
"<channel> :<topic>"
- When sending a TOPIC message to determine the
channel topic, one of two replies is sent. If
the topic is set, RPL_TOPIC is sent back else
RPL_NOTOPIC.
341 RPL_INVITING
"<channel> <nick>"
- Returned by the server to indicate that the
attempted INVITE message was successful and is
being passed onto the end client.
342 RPL_SUMMONING
"<user> :Summoning user to IRC"
- Returned by a server answering a SUMMON message to
indicate that it is summoning that user.
351 RPL_VERSION
"<version>.<debuglevel> <server> :<comments>"
- Reply by the server showing its version details.
The <version> is the version of the software being
--------------------------------------------------------------------------------
Page 51
used (including any patchlevel revisions) and the
<debuglevel> is used to indicate if the server is
running in "debug mode".
The "comments" field may contain any comments about the version or further version details.
352 RPL_WHOREPLY
"<channel> <user> <host> <server> <nick> \
<H|G>[*][@|+] :<hopcount> <real name>"
315 RPL_ENDOFWHO
"<name> :End of /WHO list"
- The RPL_WHOREPLY and RPL_ENDOFWHO pair are used
to answer a WHO message. The RPL_WHOREPLY is only
sent if there is an appropriate match to the WHO
query. If there is a list of parameters supplied
with a WHO message, a RPL_ENDOFWHO must be sent
after processing each list item with <name> being
the item.
353 RPL_NAMREPLY
"<channel> :[[@|+]<nick> [[@|+]<nick> [...]]]"
366 RPL_ENDOFNAMES
"<channel> :End of /NAMES list"
- To reply to a NAMES message, a reply pair consisting
of RPL_NAMREPLY and RPL_ENDOFNAMES is sent by the
server back to the client. If there is no channel
found as in the query, then only RPL_ENDOFNAMES is
returned. The exception to this is when a NAMES
message is sent with no parameters and all visible
channels and contents are sent back in a series of
RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark
the end.
364 RPL_LINKS
"<mask> <server> :<hopcount> <server info>"
365 RPL_ENDOFLINKS
"<mask> :End of /LINKS list"
- In replying to the LINKS message, a server must send
replies back using the RPL_LINKS numeric and mark the
end of the list using an RPL_ENDOFLINKS reply.
367 RPL_BANLIST
"<channel> <banid>"
368 RPL_ENDOFBANLIST
--------------------------------------------------------------------------------
Page 52
"<channel> :End of channel ban list"
- When listing the active 'bans' for a given channel,
a server is required to send the list back using the
RPL_BANLIST and RPL_ENDOFBANLIST messages. A separate
RPL_BANLIST is sent for each active banid. After the
banids have been listed (or if none present) a
RPL_ENDOFBANLIST must be sent.
371 RPL_INFO
":<string>"
374 RPL_ENDOFINFO
":End of /INFO list"
- A server responding to an INFO message is required to
send all its 'info' in a series of RPL_INFO messages
with a RPL_ENDOFINFO reply to indicate the end of the
replies.
375 RPL_MOTDSTART
":- <server> Message of the day - "
376 RPL_ENDOFMOTD
":End of /MOTD command"
- When responding to the MOTD message and the MOTD file
is found, the file is displayed line by line, with
each line no longer than 80 characters, using
RPL_MOTD format replies. These should be surrounded
by a RPL_MOTDSTART (before the RPL_MOTDs) and an
RPL_ENDOFMOTD (after).
381 RPL_YOUREOPER
":You are now an IRC operator"
- RPL_YOUREOPER is sent back to a client which has
just successfully issued an OPER message and gained
operator status.
382 RPL_REHASHING
"<config file> :Rehashing"
- If the REHASH option is used and an operator sends
a REHASH message, an RPL_REHASHING is sent back to
the operator.
391 RPL_TIME
--------------------------------------------------------------------------------
Page 53
"<server> :<string showing server's local time>"
- When replying to the TIME message, a server must send
the reply using the RPL_TIME format above. The string
showing the time need only contain the correct day and
time there. There is no further requirement for the
time string.
392 RPL_USERSSTART
":UserID Terminal Host"
393 RPL_USERS
":%-8s %-9s %-8s"
394 RPL_ENDOFUSERS
":End of users"
395 RPL_NOUSERS
":Nobody logged in"
- If the USERS message is handled by a server, the
replies RPL_USERSTART, RPL_USERS, RPL_ENDOFUSERS and
RPL_NOUSERS are used. RPL_USERSSTART must be sent
first, following by either a sequence of RPL_USERS
or a single RPL_NOUSER. Following this is
RPL_ENDOFUSERS.
200 RPL_TRACELINK
"Link <version & debug level> <destination> \
<next server>"
201 RPL_TRACECONNECTING
"Try. <class> <server>"
202 RPL_TRACEHANDSHAKE
"H.S. <class> <server>"
203 RPL_TRACEUNKNOWN
"???? <class> [<client IP address in dot form>]"
204 RPL_TRACEOPERATOR
"Oper <class> <nick>"
205 RPL_TRACEUSER
"User <class> <nick>"
206 RPL_TRACESERVER
"Serv <class> <int>S <int>C <server> \
<nick!user|*!*>@<host|server>"
208 RPL_TRACENEWTYPE
"<newtype> 0 <client name>"
261 RPL_TRACELOG
"File <logfile> <debug level>"
- The RPL_TRACE* are all returned by the server in
response to the TRACE message. How many are
returned is dependent on the the TRACE message and
--------------------------------------------------------------------------------
Page 54
whether it was sent by an operator or not. There is no predefined order for which occurs first. Replies RPL_TRACEUNKNOWN, RPL_TRACECONNECTING and RPL_TRACEHANDSHAKE are all used for connections which have not been fully established and are either unknown, still attempting to connect or in the process of completing the 'server handshake'. RPL_TRACELINK is sent by any server which handles a TRACE message and has to pass it on to another server. The list of RPL_TRACELINKs sent in response to a TRACE command traversing the IRC network should reflect the actual connectivity of the servers themselves along that path.
RPL_TRACENEWTYPE is to be used for any connection which does not fit in the other categories but is being displayed anyway.
211 RPL_STATSLINKINFO
"<linkname> <sendq> <sent messages> \
<sent bytes> <received messages> \
<received bytes> <time open>"
212 RPL_STATSCOMMANDS
"<command> <count>"
213 RPL_STATSCLINE
"C <host> * <name> <port> <class>"
214 RPL_STATSNLINE
"N <host> * <name> <port> <class>"
215 RPL_STATSILINE
"I <host> * <host> <port> <class>"
216 RPL_STATSKLINE
"K <host> * <username> <port> <class>"
218 RPL_STATSYLINE
"Y <class> <ping frequency> <connect \
frequency> <max sendq>"
219 RPL_ENDOFSTATS
"<stats letter> :End of /STATS report"
241 RPL_STATSLLINE
"L <hostmask> * <servername> <maxdepth>"
242 RPL_STATSUPTIME
":Server Up %d days %d:%02d:%02d"
243 RPL_STATSOLINE
"O <hostmask> * <name>"
244 RPL_STATSHLINE
"H <hostmask> * <servername>"
221 RPL_UMODEIS
"<user mode string>"
--------------------------------------------------------------------------------
Page 55
- To answer a query about a client's own mode,
RPL_UMODEIS is sent back.
251 RPL_LUSERCLIENT
":There are <integer> users and <integer> \
invisible on <integer> servers"
252 RPL_LUSEROP
"<integer> :operator(s) online"
253 RPL_LUSERUNKNOWN
"<integer> :unknown connection(s)"
254 RPL_LUSERCHANNELS
"<integer> :channels formed"
255 RPL_LUSERME
":I have <integer> clients and <integer> \
servers"
- In processing an LUSERS message, the server
sends a set of replies from RPL_LUSERCLIENT,
RPL_LUSEROP, RPL_USERUNKNOWN,
RPL_LUSERCHANNELS and RPL_LUSERME. When
replying, a server must send back
RPL_LUSERCLIENT and RPL_LUSERME. The other
replies are only sent back if a non-zero count
is found for them.
256 RPL_ADMINME
"<server> :Administrative info"
257 RPL_ADMINLOC1
":<admin info>"
258 RPL_ADMINLOC2
":<admin info>"
259 RPL_ADMINEMAIL
":<admin info>"
- When replying to an ADMIN message, a server
is expected to use replies RLP_ADMINME
through to RPL_ADMINEMAIL and provide a text
message with each. For RPL_ADMINLOC1 a
description of what city, state and country
the server is in is expected, followed by
details of the university and department
(RPL_ADMINLOC2) and finally the administrative
contact for the server (an email address here
is required) in RPL_ADMINEMAIL.
--------------------------------------------------------------------------------
Page 56
6.3 Reserved numerics.
These numerics are not described above since they fall into one of the following categories:
1 no longer in use;
2 reserved for future planned use;
3 in current use but are part of a non-generic 'feature' of
the current IRC server.
209 RPL_TRACECLASS 217 RPL_STATSQLINE
231 RPL_SERVICEINFO 232 RPL_ENDOFSERVICES
233 RPL_SERVICE 234 RPL_SERVLIST
235 RPL_SERVLISTEND
316 RPL_WHOISCHANOP 361 RPL_KILLDONE
362 RPL_CLOSING 363 RPL_CLOSEEND
373 RPL_INFOSTART 384 RPL_MYPORTIS
466 ERR_YOUWILLBEBANNED 476 ERR_BADCHANMASK
492 ERR_NOSERVICEHOST
*/
else
dprintf("%s\n", msg);
memmove(irc->bufferedinmessage, nextmsg, irc->bufferedinammount - (msg-irc->bufferedinmessage));
irc->bufferedinammount-=nextmsg-irc->bufferedinmessage;
return IRC_CONTINUE;
}
//functions above this line allow connections to multiple servers.
//it is just the control functions that only allow one server.
int IRC_Frame(int *args)
{
int stat = IRC_CONTINUE;
if (ircclient)
{
while(stat == IRC_CONTINUE)
stat = IRC_ClientFrame(ircclient);
if (stat == IRC_KILL)
{
closesocket(ircclient->socket);
IRC_Free(ircclient);
ircclient = NULL;
Con_Printf("Disconnected from irc\n");
}
}
return 0;
}
void IRC_Command(void)
{
char imsg[8192];
char *msg;
Cmd_Args(imsg, sizeof(imsg));
msg = COM_Parse(imsg);
if (*com_token == '/')
{
if (!strcmp(com_token+1, "open") || !strcmp(com_token+1, "connect"))
{
if (ircclient)
{
printf("You are already connected\nPlease /quit first\n");
return;
}
msg = COM_Parse(msg);
ircclient = IRC_Connect(com_token, 6667);
if (ircclient)
{
char username[512] = "UnknownUser";
//#ifdef _WIN32
// DWORD blen = sizeof(username)-1;
//#endif
printf("Trying to connect\n");
//#ifdef _WIN32
// GetUserName(username, &blen);
//#endif
IRC_SetPass(ircclient, "");
IRC_SetNick(ircclient, defaultnick);
IRC_SetUser(ircclient, username);
}
}
else if (!strcmp(com_token+1, "nick"))
{
msg = COM_Parse(msg);
Q_strncpyz(defaultnick, com_token, sizeof(defaultnick));
if (ircclient)
IRC_SetNick(ircclient, defaultnick);
}
else if (!strcmp(com_token+1, "list"))
{
if (ircclient)
IRC_AddClientMessage(ircclient, "LIST\r\n");
else
printf("Not connected\n");
}
else if (!strcmp(com_token+1, "join"))
{
msg = COM_Parse(msg);
if (ircclient)
IRC_AddClientMessage(ircclient, va("JOIN %s\r\n", com_token));
else
printf("Not connected\n");
}
else if (!strcmp(com_token+1, "part") || !strcmp(com_token+1, "leave"))
{
msg = COM_Parse(msg);
if (ircclient)
IRC_AddClientMessage(ircclient, va("PART %s\r\n", com_token));
else
printf("Not connected\n");
}
else if (!strcmp(com_token+1, "msg"))
{
msg = COM_Parse(msg);
if (ircclient)
IRC_AddClientMessage(ircclient, va("PRIVMSG %s :%s\r\n", com_token, msg));
else
printf("Not connected\n");
}
else if (!strcmp(com_token+1, "quote"))
{
if (ircclient)
IRC_AddClientMessage(ircclient, va("%s\r\n", msg));
else
printf("Not connected\n");
}
else if (!strcmp(com_token+1, "quit"))
{
if (ircclient)
IRC_AddClientMessage(ircclient, va("QUIT\r\n"));
else
printf("Not connected\n");
}
else if (!strcmp(com_token+1, "dest"))
{
msg = COM_Parse(msg);
Q_strncpyz(ircclient->defaultdest, com_token, sizeof(ircclient->defaultdest));
}
else if (!strcmp(com_token+1, "me"))
{
IRC_AddClientMessage(ircclient, va("PRIVMSG %s :\001ACTION%s\001\r\n", ircclient->defaultdest, msg));
}
}
else
{
if (ircclient)
{
IRC_AddClientMessage(ircclient, va("PRIVMSG %s :%s\r\n", ircclient->defaultdest, imsg));
}
else
printf("Not connected\ntype \"" COMMANDPREFIX "/open IRCSERVER\" to connect\n");
}
}