From 1ae3da7ea0fb43c7d74dfe4de716a32f365f5d14 Mon Sep 17 00:00:00 2001 From: Spoike Date: Fri, 15 Oct 2004 00:42:09 +0000 Subject: [PATCH] Simple but functional IRC client. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@340 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- plugins/irc/ircclient.c | 1230 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 1230 insertions(+) create mode 100644 plugins/irc/ircclient.c diff --git a/plugins/irc/ircclient.c b/plugins/irc/ircclient.c new file mode 100644 index 000000000..23f091cf8 --- /dev/null +++ b/plugins/irc/ircclient.c @@ -0,0 +1,1230 @@ +//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 + + + + + #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 + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef sun +#include +#endif + +#ifdef NeXT +#include +#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 + " :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 key already set" + 471 ERR_CHANNELISFULL + " :Cannot join channel (+l)" + 472 ERR_UNKNOWNMODE + " :is unknown mode char to me" + 473 ERR_INVITEONLYCHAN + " :Cannot join channel (+i)" + 474 ERR_BANNEDFROMCHAN + " :Cannot join channel (+b)" + 475 ERR_BADCHANNELKEY + " :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 + " :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 format used by USERHOST to list replies to + the query list. The reply string is composed as + follows: + + ::= ['*'] '=' <'+'|'-'> + +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 + ":[ {}]" + + - Reply format used by ISON to list replies to the + query list. + + 301 RPL_AWAY + " :" + + + +-------------------------------------------------------------------------------- +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 + " * :" + 312 RPL_WHOISSERVER + " :" + 313 RPL_WHOISOPERATOR + " :is an IRC operator" + 317 RPL_WHOISIDLE + " :seconds idle" + 318 RPL_ENDOFWHOIS + " :End of /WHOIS list" + 319 RPL_WHOISCHANNELS + " :{[@|+]}" + + - 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 + " * :" + 369 RPL_ENDOFWHOWAS + " :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 + " <# visible> :" + 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 + " " + + 331 RPL_NOTOPIC + " :No topic is set" + 332 RPL_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 + " " + + - Returned by the server to indicate that the + attempted INVITE message was successful and is + being passed onto the end client. + + 342 RPL_SUMMONING + " :Summoning user to IRC" + + - Returned by a server answering a SUMMON message to + indicate that it is summoning that user. + + 351 RPL_VERSION + ". :" + + - Reply by the server showing its version details. + The is the version of the software being + + + +-------------------------------------------------------------------------------- +Page 51 +used (including any patchlevel revisions) and the + + 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 + " \ + [*][@|+] : " + 315 RPL_ENDOFWHO + " :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 being + the item. + + 353 RPL_NAMREPLY + " :[[@|+] [[@|+] [...]]]" + 366 RPL_ENDOFNAMES + " :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 + " : " + 365 RPL_ENDOFLINKS + " :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 + " " + 368 RPL_ENDOFBANLIST + + + +-------------------------------------------------------------------------------- +Page 52 +" :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 + ":" + 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 + ":- 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 + " :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 +" :" + + + - 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 \ + " + 201 RPL_TRACECONNECTING + "Try. " + 202 RPL_TRACEHANDSHAKE + "H.S. " + 203 RPL_TRACEUNKNOWN + "???? []" + 204 RPL_TRACEOPERATOR + "Oper " + 205 RPL_TRACEUSER + "User " + 206 RPL_TRACESERVER + "Serv S C \ + @" + 208 RPL_TRACENEWTYPE + " 0 " + 261 RPL_TRACELOG + "File " + + - 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 + " \ + \ +