1
0
Fork 0
forked from fte/fteqw
fteqw/plugins/irc/ircclient.c
Spoike 0dbb57dd5c Fix some omissions with splitscreen+csqc:
unicast svc_cgamepackets will now report which seat they were unicast to.
sendevent will now reveal the seat that was active at the time of the call.
csqc console commands will now reveal any p2 etc commands that were used.
added 144hz refresh rate option to the built-in menu
added sv_demo_write_csqc cvar to write csprogs.dat into mvds. I still need to read them...
make irc client not create new accounts if there's already a connection registered for the given server.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5169 fc73d0e0-1445-4013-8a0c-d673dee63da5
2017-11-16 22:20:40 +00:00

2644 lines
68 KiB
C

//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
// changes name to while in channel
// mode command
// Spike can you implement nick tab completion. ~moodles
// need option for whois on receiving PM
// bug: setting channel to private, crashes fte when trying to join it.
// http://www.mirc.net/raws/
// http://www.ircle.com/reference/commands.shtml
#include "../plugin.h"
#include <time.h>
#include <ctype.h>
#include "../../engine/common/netinc.h"
#define handleisvalid(h) ((h)>=0)
#define invalid_handle -1
enum tlsmode_e
{
TLS_OFF,
TLS_INITIAL, //tls only
TLS_START, //tls upgrade
TLS_STARTING //don't send any nick/user/pass info while this is set
};
#define irccvars "IRC Console Variables"
vmcvar_t irc_debug = {"irc_debug", "0", irccvars, 0};
vmcvar_t irc_motd = {"irc_motd", "0", irccvars, 0};
vmcvar_t irc_nick = {"irc_nick", "", irccvars, 0};
vmcvar_t irc_altnick = {"irc_altnick", "unnamed", irccvars, 0};
vmcvar_t irc_realname = {"irc_realname", "FTE IRC-Plugin", irccvars, 0};
vmcvar_t irc_hostname = {"irc_hostname", "localhost", irccvars, 0};
vmcvar_t irc_ident = {"irc_ident", "FTE", irccvars, 0};
vmcvar_t irc_timestamp = {"irc_timestamp", "0", irccvars, 0};
vmcvar_t irc_quitmessage = {"irc_quitmessage", "", irccvars, 0};
#undef irccvars
vmcvar_t *cvarlist[] ={
&irc_debug,
&irc_motd,
&irc_nick,
&irc_altnick,
&irc_realname,
&irc_hostname,
&irc_ident,
&irc_timestamp,
&irc_quitmessage,
NULL
};
static icefuncs_t *piceapi;
static int next_window_x;
static int next_window_y;
static qboolean reloadconfig;
//static char commandname[64]; // belongs to magic tokenizer
static char subvar[9][1000]; // etghack
static char casevar[9][1000]; //numbered_command
//static char servername[64]; // store server name
#define CURRENTCONSOLE "" // need to make this the current console
#define DEFAULTCONSOLE ""
#define COMMANDNAME "irc"
#define RELEASE __DATE__
void (*Con_TrySubPrint)(const char *subname, const char *text);
void Con_FakeSubPrint(const char *subname, const char *text)
{
pCon_Print(text);
}
//porting zone:
#define COLOURGREEN "^2"
#define COLORWHITE "^7"
#define COLOURWHITE "^7" // word
#define COLOURRED "^1"
#define COLOURYELLOW "^3"
#define COLOURPURPLE "^5"
#define COLOURBLUE "^4"
#define COLOURINDIGO "^6"
#define IRC_Malloc malloc
#define IRC_Free free
#undef COM_Parse
static char *COM_Parse (char *data, char *token_out, int token_maxlen) //this is taken out of quake
{
int c;
int len;
len = 0;
token_out[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 >= token_maxlen-1)
return data;
c = *data++;
if (c=='\"' || !c)
{
token_out[len] = 0;
return data;
}
token_out[len] = c;
len++;
}
}
// parse a regular word
do
{
if (len >= token_maxlen-1)
return data;
token_out[len] = c;
data++;
len++;
c = *data;
} while (c>32);
token_out[len] = 0;
return data;
}
//\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
#define IRC_MAXOUTBUFFER (IRC_MAXMSGLEN*16)
char defaultuser[IRC_MAXNICKLEN+1] = "Unknown";
typedef struct ircclient_s {
struct ircclient_s *next;
char id[16]; //used for console prints, so we can match up consoles and clients.
char server[64];
int port;
qhandle_t socket;
enum tlsmode_e tlsmode;
qboolean quitting;
qboolean connecting;
char nick[IRC_MAXNICKLEN];
char pwd[128];
char realname[128]; //this is your OS user account... supposedly.
char hostname[128]; //this is your OS hostname... supposedly.
char autochannels[256];
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;
char bufferedoutmessage[IRC_MAXOUTBUFFER+1]; //there is a max size for protocol. (conveinient eh?) (and it's text format)
int bufferedoutammount;
struct ircice_s
{
struct ircice_s *next;
enum iceproto_e type;
char peer[IRC_MAXNICKLEN];
qboolean host; //the host is the person that initiated the call/etc. they're the ones responsible for resolving deadlocks.
qboolean allowed; //user allowed it. woot.
qboolean accepted; //peer accepted it. connection is active.
struct icestate_s *ice;
} *ice;
} ircclient_t;
ircclient_t *ircclients;
void IRC_SetFooter(ircclient_t *irc, const char *subname, const char *format, ...)
{
va_list argptr;
static char string[1024];
char lwr[128];
int i;
const char *channame = subname;
va_start (argptr, format);
Q_vsnprintf (string, sizeof(string), format,argptr);
va_end (argptr);
if (irc)
{
Q_strlcpy(lwr, irc->id, sizeof(lwr));
for (i = strlen(lwr); *subname && i < sizeof(lwr)-2; i++, subname++)
{
if (*subname >= 'A' && *subname <= 'Z')
lwr[i] = *subname - 'A' + 'a';
else
lwr[i] = *subname;
}
lwr[i] = '\0';
if (BUILTINISVALID(Con_SetConsoleFloat) && pCon_GetConsoleFloat(lwr, "iswindow") < true)
{
pCon_SetConsoleString(lwr, "title", *channame?channame:irc->server);
pCon_SetConsoleString(lwr, "prompt", "^airc^a] ");
pCon_SetConsoleFloat(lwr, "iswindow", 2);
pCon_SetConsoleFloat(lwr, "forceutf8", true);
pCon_SetConsoleFloat(lwr, "wnd_w", 256);
pCon_SetConsoleFloat(lwr, "wnd_h", 320);
//lame, but whatever.
if (next_window_x + 256 > pvid.width)
{
next_window_x = 0;
next_window_y += 320;
if (next_window_y + 320 > pvid.height)
next_window_y = 0;
}
pCon_SetConsoleFloat(lwr, "wnd_x", next_window_x);
pCon_SetConsoleFloat(lwr, "wnd_y", next_window_y);
next_window_x += 256;
}
if (BUILTINISVALID(Con_SetConsoleString))
pCon_SetConsoleString(lwr, "footer", string);
}
}
qboolean IRC_WindowShown(ircclient_t *irc, const char *subname)
{
char lwr[128];
int i;
if (irc)
{
Q_strlcpy(lwr, irc->id, sizeof(lwr));
for (i = strlen(lwr); *subname && i < sizeof(lwr)-2; i++, subname++)
{
if (*subname >= 'A' && *subname <= 'Z')
lwr[i] = *subname - 'A' + 'a';
else
lwr[i] = *subname;
}
lwr[i] = '\0';
if (BUILTINISVALID(Con_SetConsoleFloat) && pCon_GetConsoleFloat(lwr, "iswindow") < true)
return false;
}
return true;
}
void IRC_Printf(ircclient_t *irc, const char *subname, const char *format, ...)
{
va_list argptr;
static char string[1024];
char lwr[128];
int i;
const char *channame = subname;
va_start (argptr, format);
Q_vsnprintf (string, sizeof(string), format,argptr);
va_end (argptr);
if (!irc)
pCon_Print(string);
else
{
Q_strlcpy(lwr, irc->id, sizeof(lwr));
for (i = strlen(lwr); *subname && i < sizeof(lwr)-2; i++, subname++)
{
if (*subname >= 'A' && *subname <= 'Z')
lwr[i] = *subname - 'A' + 'a';
else
lwr[i] = *subname;
}
lwr[i] = '\0';
if (BUILTINISVALID(Con_SetConsoleFloat) && pCon_GetConsoleFloat(lwr, "iswindow") < true)
{
pCon_SetConsoleString(lwr, "title", *channame?channame:irc->server);
pCon_SetConsoleString(lwr, "prompt", "^airc^a] ");
pCon_SetConsoleFloat(lwr, "iswindow", 2);
pCon_SetConsoleFloat(lwr, "forceutf8", true);
pCon_SetConsoleFloat(lwr, "wnd_w", 256);
pCon_SetConsoleFloat(lwr, "wnd_h", 320);
//lame, but whatever.
if (next_window_x + 256 > pvid.width)
{
next_window_x = 0;
next_window_y += 320;
if (next_window_y + 320 > pvid.height)
next_window_y = 0;
}
pCon_SetConsoleFloat(lwr, "wnd_x", next_window_x);
pCon_SetConsoleFloat(lwr, "wnd_y", next_window_y);
next_window_x += 256;
}
if (!*string)
pCon_SetActive(lwr);
Con_TrySubPrint(lwr, string);
}
}
void IRC_InitCvars(void)
{
vmcvar_t *v;
int i;
for (i=0; cvarlist[i]; i++)
{
v = cvarlist[i];
v->handle = pCvar_Register(v->name, v->string, v->flags, v->group);
}
}
int IRC_CvarUpdate(void) // perhaps void instead?
{
vmcvar_t *v;
int i;
for (i=0; cvarlist[i]; i++)
{
v = cvarlist[i];
v->modificationcount = pCvar_Update(v->handle, &v->modificationcount, v->string, &v->value);
}
return 0;
}
void IRC_Command(ircclient_t *ircclient, char *dest);
qintptr_t IRC_ExecuteCommand(qintptr_t *args);
qintptr_t IRC_ConExecuteCommand(qintptr_t *args);
qintptr_t IRC_Frame(qintptr_t *args);
qintptr_t IRC_ConsoleLink(qintptr_t *args);
qintptr_t Plug_Init(qintptr_t *args)
{
if ( Plug_Export("Tick", IRC_Frame) &&
Plug_Export("ExecuteCommand", IRC_ExecuteCommand))
{
pCmd_AddCommand(COMMANDNAME);
Plug_Export("ConsoleLink", IRC_ConsoleLink);
if (!Plug_Export("ConExecuteCommand", IRC_ConExecuteCommand))
{
pCon_Print("IRC Client Plugin Loaded in single-console mode\n");
Con_TrySubPrint = Con_FakeSubPrint;
}
else
{
pCon_Print("IRC Client Plugin Loaded. ^[/"COMMANDNAME" /help^] for help\n");
Con_TrySubPrint = pCon_SubPrint;
}
reloadconfig = true;
IRC_InitCvars();
Q_strlcpy(defaultuser, "FTEUser", sizeof(defaultuser));
if (BUILTINISVALID(Plug_GetNativePointer))
piceapi = pPlug_GetNativePointer(ICE_API_CURRENT);
return true;
}
else
{
pCon_Print("IRC Client Plugin failed\n");
}
return false;
}
qintptr_t IRC_ExecuteCommand(qintptr_t *args)
{
char cmd[256];
pCmd_Argv(0, cmd, sizeof(cmd));
if (!strcmp(cmd, COMMANDNAME))
{
ircclient_t *ircclient = ircclients;
//FIXME: select an irc network more inteligently
IRC_Command(ircclient, ircclient?ircclient->defaultdest:"");
return true;
}
return false;
}
qintptr_t IRC_ConExecuteCommand(qintptr_t *args)
{
char buffer[256];
ircclient_t *ircclient;
//FIXME: select the right network
pCmd_Argv(0, buffer, sizeof(buffer));
//buffer is something like: irc53:#foo
for (ircclient = ircclients; ircclient; ircclient = ircclient->next)
{
if (!strncmp(ircclient->id, buffer, strlen(ircclient->id)))
break;
}
if (!ircclient)
{
if (*buffer == '/')
IRC_Command(NULL, "");
else
Con_TrySubPrint(buffer, "You were disconnected\n");
return true;
}
IRC_Command(ircclient, buffer+strlen(ircclient->id));
return true;
}
void IRC_AddClientMessage(ircclient_t *irc, char *msg)
{
char output[4096];
int len;
Q_strlcpy(output, msg, sizeof(output));
Q_strlcat(output, "\n", sizeof(output));
len = strlen(output);
if (irc->bufferedoutammount + len > sizeof(irc->bufferedoutmessage))
return;
memcpy(irc->bufferedoutmessage + irc->bufferedoutammount, output, len);
irc->bufferedoutammount += len;
if (irc_debug.value == 1) { IRC_Printf(irc, DEFAULTCONSOLE,COLOURYELLOW "<< %s \n",msg); }
}
ircclient_t *IRC_FindAccount(const char *server)
{
ircclient_t *irc;
for (irc = ircclients; irc; irc = irc->next)
{
if (!strcmp(irc->server, server))
return irc;
}
return NULL; //no match
}
ircclient_t *IRC_Create(const char *server, const char *nick, const char *realname, const char *hostname, const char *password, const char *channels)
{
ircclient_t *irc;
//FIXME: accept server:+port for starttls
//FIXME: accept server:*port for initial tls
irc = IRC_Malloc(sizeof(ircclient_t));
if (!irc)
return NULL;
memset(irc, 0, sizeof(ircclient_t));
Q_snprintf(irc->id, sizeof(irc->id), "IRC%x%x:", rand(),rand());
irc->connecting = true;
irc->tlsmode = TLS_OFF;
irc->quitting = false;
irc->socket = invalid_handle;
Q_strlcpy(irc->server, server, sizeof(irc->server));
IRC_CvarUpdate();
strcpy(irc->nick, nick);
strcpy(irc->realname, realname);
strcpy(irc->hostname, hostname);
strcpy(irc->pwd, password);
strcpy(irc->autochannels, channels);
// gethostname(irc->hostname, sizeof(irc->hostname));
// irc->hostname[sizeof(irc->hostname)-1] = 0;
irc->next = ircclients;
ircclients = irc;
return irc;
}
void IRC_SetPass(ircclient_t *irc, char *pass)
{
Q_strlcpy(irc->pwd, pass, sizeof(irc->pwd));
if (*pass && irc->tlsmode != TLS_STARTING)
IRC_AddClientMessage(irc, va("PASS %s", pass));
}
void IRC_SetNick(ircclient_t *irc, char *nick)
{
Q_strlcpy(irc->nick, nick, sizeof(irc->nick));
if (irc->tlsmode != TLS_STARTING)
IRC_AddClientMessage(irc, va("NICK %s", irc->nick));
}
void IRC_SetUser(ircclient_t *irc, char *user)
{
IRC_CvarUpdate();
if (irc->tlsmode != TLS_STARTING)
IRC_AddClientMessage(irc, va("USER %s %s %s :%s", irc_ident.string, irc->hostname, irc->server, irc_realname.string));
}
qboolean IRC_Establish(ircclient_t *irc)
{
if (!irc)
return false;
if (handleisvalid(irc->socket)) //don't need to do anything.
return true;
//clear up any stale state
irc->bufferedoutammount = 0;
irc->bufferedinammount = 0;
irc->quitting = false;
irc->socket = pNet_TCPConnect(irc->server, 6667); //port is only used if the url doesn't contain one. It's a default.
//not yet blocking. So no frequent attempts please...
//non blocking prevents connect from returning worthwhile sensible value.
if (!handleisvalid(irc->socket))
{
Con_Printf("IRC_OpenSocket: couldn't connect\n");
return false;
}
if (irc->tlsmode == TLS_INITIAL)
{
if (pNet_SetTLSClient(irc->socket, irc->server) < 0)
{
pNet_Close(irc->socket);
irc->socket = invalid_handle;
return false;
}
}
else if (irc->tlsmode != TLS_OFF)
{
IRC_AddClientMessage(irc, "STARTTLS");
irc->tlsmode = TLS_STARTING;
}
else
{
IRC_SetPass(irc, irc->pwd);
IRC_SetNick(irc, irc->nick);
IRC_SetUser(irc, defaultuser);
}
return true;
}
void IRC_ParseConfig(void)
{
qhandle_t config;
int len = pFS_Open("**plugconfig", &config, 1);
if (len >= 0)
{
char *buf = malloc(len+1);
char *msg = buf;
buf[len] = 0;
pFS_Read(config, buf, len);
pFS_Close(config);
while (msg && *msg)
{
ircclient_t *irc;
char server[256];
char channels[1024];
char nick[256];
char password[256];
char user[256];
char hostname[256];
msg = COM_Parse(msg, server, sizeof(server));
msg = COM_Parse(msg, channels, sizeof(channels));
msg = COM_Parse(msg, nick, sizeof(nick));
msg = COM_Parse(msg, password, sizeof(password));
msg = COM_Parse(msg, user, sizeof(user));
msg = COM_Parse(msg, hostname, sizeof(hostname));
if (*server)
{
irc = IRC_Create(server, nick, user, hostname, password, channels);
if (irc)
{
if (IRC_Establish(irc))
{
if (!*irc->autochannels)
IRC_Printf(irc, DEFAULTCONSOLE, "Trying to connect to %s\n", irc->server);
}
else
IRC_Printf(irc, DEFAULTCONSOLE, "Unable to connect to %s\n", irc->server);
}
}
}
free(buf);
}
}
void IRC_WriteConfig(void)
{
qhandle_t config;
pFS_Open("**plugconfig", &config, 2);
if (config >= 0)
{
ircclient_t *irc;
for(irc = ircclients; irc; irc = irc->next)
{
char *s = va("\"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n", irc->server, irc->autochannels, irc->nick, irc->pwd, irc->realname, irc->hostname);
if (irc->quitting)
continue;
pFS_Write(config, s, strlen(s));
}
pFS_Close(config);
}
}
void IRC_PartChannelInternal(ircclient_t *irc, char *channelname)
{
char ac[countof(irc->autochannels)];
char *chan;
strcpy(ac, irc->autochannels);
chan = strtok(ac, " ");
*irc->autochannels = 0;
while(chan)
{
if (*chan)
{
char *pwd = strchr(chan, ',');
if (pwd)
*pwd++ = 0;
if (strcmp(chan, channelname))
{
if (*irc->autochannels)
Q_strncatz(irc->autochannels, " ", sizeof(irc->autochannels));
if (pwd)
Q_strncatz(irc->autochannels, va("%s,%s", chan, pwd), sizeof(irc->autochannels));
else
Q_strncatz(irc->autochannels, va("%s", chan), sizeof(irc->autochannels));
}
}
chan = strtok(NULL, " ");
}
}
void IRC_PartChannel(ircclient_t *irc, char *channelname)
{
IRC_PartChannelInternal(irc, channelname);
IRC_AddClientMessage(irc, va("PART %s", channelname));
}
void IRC_JoinChannel(ircclient_t *irc, char *channel, char *key) // i screwed up, its actually: <channel>{,<channel>} [<key>{,<key>}]
{
IRC_PartChannelInternal(irc, channel);
if (*irc->autochannels)
Q_strncatz(irc->autochannels, " ", sizeof(irc->autochannels));
Q_strncatz(irc->autochannels, va("%s,%s", channel, key), sizeof(irc->autochannels));
if (key)
{
/*if (*channel != '#')
IRC_AddClientMessage(irc, va("JOIN #%s %s", channel,key));
else*/
IRC_AddClientMessage(irc, va("JOIN %s %s", channel,key));
}
else
{
/*if (*channel != '#')
IRC_AddClientMessage(irc, va("JOIN #%s", channel));
else*/
IRC_AddClientMessage(irc, va("JOIN %s", channel));
}
}
void IRC_JoinChannels(ircclient_t *irc, char *channelstring)
{
char *chan = strtok(channelstring, " ");
while(chan)
{
if (*chan)
{
char *line = va("JOIN %s", chan);
char *comma = strchr(line, ',');
if (comma)
*comma = ' ';
IRC_AddClientMessage(irc, line);
}
chan = strtok(NULL, " ");
}
}
/*
ATTN: Spike
# (just for reference) == Ctrl+K in mirc to put the color code symbol in
now to have a background color, you must specify a forground color first (#0,15)
, denotes end of forground color, and start of background color
irc colors work in many strange ways:
#0-#15 for forground color // the code currently converts to this one, which is not the "proper" irc way, read the next one to understand. Still need to support it, just not output as it.
#00-#15 for forground color (note #010 to #015 is not valid) --- this is the "proper" irc way, because I could say "#11+1=2" (which means I want 1+1=2 to appear black (1), but instead it will come out as indigo (11) and look like this: +1=2)
background examples: (note
#0,15 (white forground, light gray background)
#00,15 (white forground, light gray background) // proper way
#15,0 (white forground, light gray background)
#15,00 (white forground, light gray background) // proper way
I hope this makes sense to you, to be able to edit the IRC_FilterMircColours function ~ Moodles
*/
void IRC_FilterMircColours(char *msg)
{
int i;
int chars;
while(*msg)
{
if (*msg == 3)
{
chars = 2;
if (msg[1] >= '0' && msg[1] <= '9')
{
i = msg[1]- '0';
if (msg[2] >= '0' && msg[2] <= '9')
{
i = i*10 + (msg[2]-'0');
chars = 3;
}
}
else
i = msg[1];
switch(i)
{
case 0:
msg[1] = '7'; //white
break;
case 1:
msg[1] = '0'; //black
break;
case 2:
msg[1] = '4'; //darkblue
break;
case 3:
msg[1] = '2'; //darkgreen
break;
case 4:
msg[1] = '1'; //red
break;
case 5:
msg[1] = '1'; //brown
break;
case 6:
msg[1] = '5'; //purple
break;
case 7:
msg[1] = '3'; //orange
break;
case 8:
msg[1] = '3'; //yellow
break;
case 9:
msg[1] = '2'; //lightgreen
break;
case 10:
msg[1] = '6'; //darkcyan
break;
case 11:
msg[1] = '6'; //lightcyan
break;
case 12:
msg[1] = '4'; //lightblue
break;
case 13:
msg[1] = '5'; //pink
break;
case 14:
msg[1] = '7'; //grey
break;
case 15:
msg[1] = '7'; //lightgrey
break;
default:
msg++;
continue;
}
*msg = '^';
msg+=2;
if (chars==3)
memmove(msg, msg+1, strlen(msg));
continue;
}
msg++;
}
}
#define IRC_DONE 0
#define IRC_CONTINUE 1
#define IRC_KILL 2
void magic_tokenizer(int word,char *thestring)
{
char *temp;
int i = 1;
strcpy(casevar[1],thestring);
temp = strchr(casevar[1], ' ');
while (i < 8)
{
i++;
if (temp != NULL)
{
strcpy(casevar[i],temp+1);
}
else
{
strcpy(casevar[i], "");
}
temp=strchr(casevar[i], ' ');
}
}
void magic_etghack(char *thestring)
{
char *temp;
int i = 1;
strcpy(subvar[1],thestring);
temp = strchr(subvar[1], ' ');
while (i < 8)
{
i++;
if (temp != NULL)
{
strcpy(subvar[i],temp+1);
}
else
{
strcpy(subvar[i], "");
}
temp=strchr(subvar[i], ' ');
}
}
//==================================================
void numbered_command(int comm, char *msg, ircclient_t *irc) // move vars up 1 more than debug says
{
magic_tokenizer(0,msg);
switch (comm)
{
case 1: /* RPL_WELCOME */
case 2: /* RPL_YOURHOST */
case 3: /* RPL_CREATED */
case 4: /* RPL_MYINFO */
case 5: /* RPL_ISUPPORT */
{
if (irc->tlsmode != TLS_STARTING)
irc->connecting = 0; // ok we are connected
if (irc_motd.value)
IRC_Printf(irc, DEFAULTCONSOLE, COLOURYELLOW "SERVER STATS: %s\n",casevar[3]);
return;
}
// case 5: /* RPL_BOUNCE */
// {
// IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "RPL_BOUNCE: %s\n",casevar[3]);
// return;
// }
case 20: /* RPL_HELLO */
{
if (irc_motd.value)
IRC_Printf(irc, DEFAULTCONSOLE, COLOURYELLOW "%s\n",casevar[3]);
return;
}
case 42: /*RPL_YOURID */
{
if (irc_motd.value)
IRC_Printf(irc, DEFAULTCONSOLE, COLOURYELLOW "%s\n",casevar[3]);
return;
}
case 250:
case 251: /* RPL_LUSERCLIENT */
case 252: /* RPL_LUSEROP */
case 253: /* RPL_LUSERUNKNOWN */
case 254: /* RPL_LUSERCHANNELS */
case 255: /* RPL_LUSERME */
case 256: /* RPL_ADMINME */
case 257: /* RPL_ADMINLOC1 */
case 258: /* RPL_ADMINLOC2 */
case 259: /* RPL_ADMINEMAIL */
case 265:
case 266:
{
if (irc_motd.value)
IRC_Printf(irc, DEFAULTCONSOLE, COLOURYELLOW "SERVER STATS: %s\n",casevar[3]);
return;
}
case 301: /* #define RPL_AWAY 301 */
{
char *username = strtok(casevar[3], " ");
char *awaymessage = casevar[4]+1;
IRC_Printf(irc, DEFAULTCONSOLE,"WHOIS: <%s> (Away Message: %s)\n",username,awaymessage);
return;
}
case 305: /* RPL_UNAWAY */
case 306: /* RPL_NOWAWAY */
{
char *away = casevar[3]+1;
IRC_Printf(irc, CURRENTCONSOLE,"%s\n",away);
return;
}
case 311: /* #define RPL_WHOISUSER 311 */
{
char *username = strtok(casevar[3], " ");
char *ident = strtok(casevar[4], " ");
char *address = strtok(casevar[5], " ");
char *realname = casevar[7]+1;
IRC_Printf(irc, DEFAULTCONSOLE,"WHOIS: <%s> (Ident: %s) (Address: %s) (Realname: %s) \n", username, ident, address, realname);
return;
}
case 312: /* #define RPL_WHOISSERVER 312 */ //seems to be /whowas also
{
char *username = strtok(casevar[3], " ");
char *serverhostname = strtok(casevar[4], " ");
char *servername = casevar[5]+1;
IRC_Printf(irc, DEFAULTCONSOLE,"WHOIS: <%s> (Server: %s) (Server Name: %s) \n", username, serverhostname, servername);
return;
}
case 313: /* RPL_WHOISOPERATOR */
{
char *username = strtok(casevar[3], " ");
char *isoperator = casevar[4]+1;
IRC_Printf(irc, DEFAULTCONSOLE,"WHOIS: <%s> (%s)\n", username,isoperator);
return;
}
case 317: /* #define RPL_WHOISIDLE 317 */
{
char *username = strtok(casevar[3], " ");
char *secondsidle = strtok(casevar[4], " ");
char *signontime = strtok(casevar[5], " ");
time_t t;
const struct tm *tm;
char buffer[100];
t=strtoul(signontime, 0, 0);
tm=localtime(&t);
strftime (buffer, 100, "%a %b %d %H:%M:%S", tm);
IRC_Printf(irc, DEFAULTCONSOLE,"WHOIS: <%s> (Idle Time: %s seconds) (Signon Time: %s) \n", username, secondsidle, buffer);
return;
}
case 318: /* #define RPL_ENDOFWHOIS 318 */
{
char *endofwhois = casevar[4]+1;
IRC_Printf(irc, DEFAULTCONSOLE,"WHOIS: %s\n", endofwhois);
return;
}
case 319: /* #define RPL_WHOISCHANNELS 319 */
{
char *username = strtok(casevar[3], " ");
char *channels = casevar[4]+1;
IRC_Printf(irc, DEFAULTCONSOLE,"WHOIS: <%s> (Channels: %s)\n",username,channels); // need to remove the space from the end of channels
return;
}
case 321:
{
IRC_Printf(irc, "list", "Start /LIST\n");
return;
}
case 322: /* #define RPL_LIST 322 */
{
char *channel = strtok(casevar[3], " ");
char *users = strtok(casevar[4], " ");
char *topic = casevar[5]+1;
IRC_Printf(irc, "list", "^1Channel:^7 %s ^1Users:^7 %s ^1Topic:^7 %s\n\n", channel,users,topic);
return;
}
case 323: /* RPL_LISTEND*/
{
char *endoflist = casevar[3]+1;
IRC_Printf(irc, "list", "%s\n",endoflist);
return;
}
case 366: /* RPL_ENDOFNAMES */
{
char *channel = strtok(casevar[3], " ");
char *endofnameslist = casevar[4]+1;
IRC_Printf(irc, channel,"%s\n",endofnameslist);
return;
}
case 372: /* RPL_MOTD */
case 375: /* RPL_MOTDSTART */
case 376: /* RPL_ENDOFMOTD */
{
char *motdmessage = casevar[3]+1;
IRC_CvarUpdate();
if (irc_motd.value == 2)
IRC_Printf(irc, DEFAULTCONSOLE, "MOTD: %s\n", motdmessage);
else if (irc_motd.value)
IRC_Printf(irc, DEFAULTCONSOLE, "%s\n", motdmessage);
if (*irc->autochannels)
IRC_JoinChannels(irc, irc->autochannels);
return;
}
case 378:
{
IRC_Printf(irc, DEFAULTCONSOLE, "%s\n", msg);
return;
}
case 401: /* ERR_NOSUCHNICK */
case 403: /* ERR_NOSUCHCHANNEL */
case 404: /* ERR_CANNOTSENDTOCHAN */
case 405: /* ERR_TOOMANYCHANNELS */
{
char *username = strtok(casevar[3], " ");
char *error = casevar[4]+1;
IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "ERROR <%s>: %s\n",username,error);
return;
}
case 432: /* #define ERR_ERRONEUSNICKNAME 432 */
{
IRC_Printf(irc, DEFAULTCONSOLE, "Erroneous/invalid nickname given\n");
return;
}
case 433: /* #define ERR_NICKNAMEINUSE 433 */
case 438:
case 453:
{
char *nickname = strtok(casevar[4], " ");
char *badnickname = ":Nickname";
char *seedednick;
if ( !strcasecmp(nickname,badnickname) ) // bug with ircd, the nickname actually shifts position.
{
nickname = strtok(casevar[3], " ");
}
IRC_CvarUpdate();
IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "ERROR: <%s> is already in use.\n",nickname);
if (irc->tlsmode == TLS_STARTING)
{
//don't submit any of this info here.
}
else if ( !strcmp(nickname,irc_nick.string) && (irc->connecting == 1) )
{
IRC_SetNick(irc, irc_altnick.string);
}
else if ( !strcmp(nickname, irc_altnick.string) && (irc->connecting == 1) )
{
IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "ERROR: <%s> AND <%s> both in use. Attempting generic nickname.\n",irc_nick.string,irc_altnick.string);
seedednick = va("%s%i", irc_nick.string, rand());
IRC_SetNick(irc, seedednick);
}
else
{
if (irc->connecting == 1)
{
seedednick = va("FTE%i",rand());
IRC_SetNick(irc, seedednick);
}
}
return;
}
case 471: /* ERR_CHANNELISFULL */
{
char *channel = strtok(casevar[3], " ");
char *error = casevar[4]+1;
IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "ERROR: <%s>: %s (Channel is full and has reached user limit)\n",channel,error);
return;
}
case 472: /* ERR_UNKNOWNMODE */
{
char *mode = strtok(casevar[3], " ");
char *error = casevar[4]+1;
IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "ERROR: <%s>: %s (Unknown mode)\n",mode,error);
return;
}
case 473: /* ERR_INVITEONLYCHAN */
{
char *channel = strtok(casevar[3], " ");
char *error = casevar[4]+1;
IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "ERROR: <%s>: %s (Invite only)\n",channel,error);
return;
}
case 474: /* ERR_BANNEDFROMCHAN */
{
char *channel = strtok(casevar[3], " ");
char *error = casevar[4]+1;
IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "ERROR: <%s>: %s (You are banned)\n",channel,error);
return;
}
case 475: /* ERR_BADCHANNELKEY */
{
char *channel = strtok(casevar[3], " ");
char *error = casevar[4]+1;
IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "ERROR: <%s>: %s (Need the correct channel key. Example: /join %s bananas)\n",channel,error,channel);
return;
}
case 482: /* ERR_CHANOPRIVSNEEDED */
{
char *channel = strtok(casevar[3], " ");
char *error = casevar[4]+1;
IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "ERROR: <%s>: %s (Need +o or @ status)\n",channel,error,channel);
return;
}
case 670: /* RPL_STARTTLS */
{
pNet_SetTLSClient(irc->socket, irc->server);
irc->tlsmode = TLS_START;
IRC_SetPass(irc, irc->pwd);
IRC_SetNick(irc, irc->nick);
IRC_SetUser(irc, defaultuser);
return;
}
case 691: /* ERR_STARTTLS */
{
IRC_Printf(irc, DEFAULTCONSOLE, COLOURYELLOW "STARTTLS Failed: %s\n", casevar[3]);
pNet_Close(irc->socket);
irc->socket = invalid_handle;
return;
}
}
IRC_Printf(irc, DEFAULTCONSOLE, "%s\n", msg); // if no raw number exists, print the thing
}
struct ircice_s *IRC_ICE_Find(ircclient_t *irc, const char *sender, enum iceproto_e type)
{
struct ircice_s *ice;
for (ice = irc->ice; ice; ice = ice->next)
{
if (ice->type == type && !strcmp(ice->peer, sender))
return ice;
}
return NULL;
}
struct ircice_s *IRC_ICE_Create(ircclient_t *irc, const char *sender, enum iceproto_e type, qboolean creator)
{
struct icestate_s *ice;
struct ircice_s *ircice;
if (!piceapi)
return NULL;
if (!creator && type == ICEP_QWSERVER)
ice = piceapi->ICE_Create(NULL, NULL, sender, ICEM_ICE, ICEP_QWCLIENT);
else if (!creator && type == ICEP_QWCLIENT)
ice = piceapi->ICE_Create(NULL, NULL, sender, ICEM_ICE, ICEP_QWSERVER);
else
ice = piceapi->ICE_Create(NULL, NULL, sender, ICEM_ICE, type);
if (!ice)
return NULL;
piceapi->ICE_Set(ice, "controller", creator?"1":"0");
if (creator && type == ICEP_VOICE)
{
//note: the engine will ignore codecs it does not support.
piceapi->ICE_Set(ice, "codec96", "opus@48000");
piceapi->ICE_Set(ice, "codec97", "speex@16000"); //wide
piceapi->ICE_Set(ice, "codec98", "speex@8000"); //narrow
piceapi->ICE_Set(ice, "codec99", "speex@32000"); //ultrawide
piceapi->ICE_Set(ice, "codec8", "pcma@8000");
piceapi->ICE_Set(ice, "codec0", "pcmu@8000");
}
//query dns to see if there's a stunserver hosted by the same domain
//nslookup -querytype=SRV _stun._udp.example.com
// Q_snprintf(stunhost, sizeof(stunhost), "_stun._udp.%s", ice->server);
// if (NET_DNSLookup_SRV(stunhost, stunhost, sizeof(stunhost)))
// piceapi->ICE_Set(ice, "stunip", stunhost);
// else
{
//irc services tend to not provide any stun info, so steal someone's... hopefully they won't mind too much. :(
piceapi->ICE_Set(ice, "stunport", "19302");
piceapi->ICE_Set(ice, "stunip", "stun.l.google.com");
}
ircice = malloc(sizeof(*ircice));
memset(ircice, 0, sizeof(*ircice));
ircice->next = irc->ice;
irc->ice = ircice;
ircice->type = type;
Q_strlcpy(ircice->peer, sender, sizeof(ircice->peer));
ircice->host = creator;
ircice->accepted = false;
ircice->allowed = creator;
ircice->ice = ice;
return ircice;
}
void IRC_ICE_Update(ircclient_t *irc, struct ircice_s *ice, char updatetype)
{
//I was originally using colons to separate terms, but switched to slashes to avoid smilies for irc clients that print unknown CTCP messages.
char message[1024];
struct icecandinfo_s *c;
char *icetype;
if (!ice->allowed && updatetype != '-')
return;
*message = 0;
switch(ice->type)
{
default:
case ICEP_VOICE:
icetype = "voice";
break;
case ICEP_QWSERVER:
icetype = "qwserver";
break;
case ICEP_QWCLIENT:
icetype = "qwclient";
break;
}
if (updatetype == '=' || updatetype == '*')
{
char ufrag[256];
char pwd[256];
piceapi->ICE_Get(ice->ice, "lufrag", ufrag, sizeof(ufrag));
piceapi->ICE_Get(ice->ice, "lpwd", pwd, sizeof(pwd));
Q_snprintf(message, sizeof(message), " ufrag/%s pwd/%s", ufrag, pwd);
}
if (updatetype == '+' || updatetype == '=')
{
unsigned int i;
for (i = 0; i <= 127; i++)
{
char codec[256];
char codecname[64];
char argn[64];
Q_snprintf(argn, sizeof(argn), "codec%i", i);
if (!piceapi->ICE_Get(ice->ice, argn, codecname, sizeof(codecname)))
continue;
if (!strcmp(codecname, "speex@8000")) //speex narrowband
Q_snprintf(codec, sizeof(codec), "codec/%i/speex/8000", i);
else if (!strcmp(codecname, "speex@16000")) //speex wideband
Q_snprintf(codec, sizeof(codec), "codec/%i/speex/16000", i);
else if (!strcmp(codecname, "speex@32000")) //speex ultrawideband
Q_snprintf(codec, sizeof(codec), "codec/%i/speex/32000", i);
else if (!strcmp(codecname, "pcma@8000")) //speex wideband
Q_snprintf(codec, sizeof(codec), "codec/%i/pcma/8000", i);
else if (!strcmp(codecname, "pcmu@8000")) //speex ultrawideband
Q_snprintf(codec, sizeof(codec), "codec/%i/pcmu/8000", i);
else if (!strcmp(codecname, "opus@48000")) //opus codec.
Q_snprintf(codec, sizeof(codec), "codec/%i/opus/48000", i);
else
continue;
if (strlen(message) + strlen(codec) + 2 > 256)
{
IRC_AddClientMessage(irc, va("NOTICE %s :\001FTEICE %c%s%s\001", ice->peer, updatetype, icetype, message));
updatetype = '%';
*message = 0;
}
Q_strlcat(message, " ", sizeof(message));
Q_strlcat(message, codec, sizeof(message));
}
}
/* if (*message)
{
IRC_AddClientMessage(irc, va("NOTICE %s :\001FTEICE %c%s%s\001", ice->peer, updatetype, icetype, message));
*message = 0;
}
*/
if (updatetype != '+' && updatetype != '-')
{
while ((c = piceapi->ICE_GetLCandidateInfo(ice->ice)))
{
char type[] = "hspr";
char cand[256];
Q_snprintf(cand, sizeof(cand), "cand/"
"%c%c/%i/%i/"
"%i/%i/%i/"
"%i/%s/%s",
type[c->type], 'u', c->priority, c->port,
c->network, c->generation, c->foundation,
c->component, c->candidateid, c->addr);
if (strlen(message) + strlen(cand) + 2 > 256)
{
IRC_AddClientMessage(irc, va("NOTICE %s :\001FTEICE %c%s%s\001", ice->peer, updatetype, icetype, message));
updatetype = '%';
*message = 0;
}
Q_strlcat(message, " ", sizeof(message));
Q_strlcat(message, cand, sizeof(message));
}
}
if (*message || updatetype != '%')
IRC_AddClientMessage(irc, va("NOTICE %s :\001FTEICE %c%s%s\001", ice->peer, updatetype, icetype, message));
}
void IRC_ICE_ParseCandidate(struct icestate_s *ice, char *cand)
{
char *addr;
struct icecandinfo_s info;
if (strlen(cand) < 12)
return;
switch(cand[5])
{
case 'h': info.type = ICE_HOST; break;
case 's': info.type = ICE_SRFLX; break;
case 'p': info.type = ICE_PRFLX; break;
default:
case 'r': info.type = ICE_RELAY; break;
}
info.transport = (cand[6] == 't')?1:0;
info.priority = strtol(cand+8, &cand, 0); if (*cand != '/')return;
info.port = strtol(cand+1, &cand, 0); if (*cand != '/')return;
info.network = strtol(cand+1, &cand, 0); if (*cand != '/')return;
info.generation = strtol(cand+1, &cand, 0); if (*cand != '/')return;
info.foundation = strtol(cand+1, &cand, 0); if (*cand != '/')return;
info.component = strtol(cand+1, &cand, 0); if (*cand != '/')return;
addr = strchr(cand+1, '/');
if (!addr)
return;
*addr++ = 0;
Q_strlcpy(info.candidateid, cand+1, sizeof(info.candidateid));
Q_strlcpy(info.addr, addr, sizeof(info.candidateid));
piceapi->ICE_AddRCandidateInfo(ice, &info);
}
void IRC_ICE_ParseCodec(struct icestate_s *ice, char *codec)
{
char *start;
unsigned int num;
char name[64];
unsigned int rate;
num = strtoul(codec+6, &codec, 0); if (*codec != '/')return;
start = codec+1; codec = strchr(codec, '/'); if (!codec)return;*codec = 0; Q_strlcpy(name, start, sizeof(name));
rate = strtoul(codec+1, &codec, 0);
Q_strlcat(name, va("@%u", rate), sizeof(name));
piceapi->ICE_Set(ice, va("codec%i", num), name);
}
void IRC_ICE_Parse(ircclient_t *irc, const char *sender, char *message)
{
struct ircice_s *ice;
char token[256];
enum iceproto_e type = ICEP_INVALID;
message = COM_Parse(message, token, sizeof(token));
if (*token == '+' || *token == '=' || *token == '*' || *token == '%')
{ //+ is offer or accept for a new content type
//= is an ack from the receiver
//* is the final handshake that includes offerer's full details
//% is extra updates.
char icetype = *token;
if (!strcmp(token+1, "voice"))
type = ICEP_VOICE;
else if (!strcmp(token+1, "qwserver"))
type = ICEP_QWSERVER;
else if (!strcmp(token+1, "qwclient"))
type = ICEP_QWCLIENT;
else
{
IRC_Printf(irc, sender, "ICE session type %s is not recognised\n", token);
return;
}
ice = IRC_ICE_Find(irc, sender, type);
if (!ice && (icetype == '+' || icetype == '='))
ice = IRC_ICE_Create(irc, sender, type, false);//icetype=='=');
if (ice)
{
while(message)
{
message = COM_Parse(message, token, sizeof(token));
if (!strncmp(token, "cand/", 5))
IRC_ICE_ParseCandidate(ice->ice, token);
else if (!strncmp(token, "codec/", 6))
IRC_ICE_ParseCodec(ice->ice, token);
else if (!strncmp(token, "ufrag/", 6))
piceapi->ICE_Set(ice->ice, "rufrag", token+6);
else if (!strncmp(token, "pwd/", 4))
piceapi->ICE_Set(ice->ice, "rpwd", token+4);
else if (*token)
IRC_Printf(irc, sender, "unknown ice token %s\n", token);
}
if ((icetype == '=' || icetype == '*') && !ice->accepted && ice->allowed)
{
piceapi->ICE_Set(ice->ice, "state", STRINGIFY(ICE_CONNECTING));
ice->accepted = true;
}
switch(icetype)
{
case '+':
//needs user
if (!ice->allowed)
{
switch(type)
{
case ICEP_VOICE: IRC_Printf(irc, sender, "%s is trying to call you. ^[[Click to Converse]\\act\\iceaccept_v\\who\\%s^] ^[[Click to Decline]\\act\\icedecline_v\\who\\%s^] \n", sender, sender, sender); break;
case ICEP_QWSERVER: IRC_Printf(irc, sender, "%s wants you to join their game. ^[[Click to Join]\\act\\iceaccept_s\\who\\%s^] ^[[Click to Decline]\\act\\icedecline_s\\who\\%s^] \n", sender, sender, sender); break;
case ICEP_QWCLIENT: IRC_Printf(irc, sender, "%s is trying to gatecrash your game. ^[[Click to Allow]\\act\\iceaccept_c\\who\\%s^] ^[[Click to Decline]\\act\\icedecline_c\\who\\%s^] \n", sender, sender, sender); break;
case ICEP_INVALID: break;
case ICEP_VIDEO: break;
}
}
else
{
switch(type)
{
case ICEP_VOICE: IRC_Printf(irc, sender, "Accepting voice call\n"); break;
case ICEP_QWSERVER: IRC_Printf(irc, sender, "Accepting game invite\n"); break;
case ICEP_QWCLIENT: IRC_Printf(irc, sender, "Accepting gatecrash\n"); break;
case ICEP_INVALID: break;
case ICEP_VIDEO: break;
}
IRC_ICE_Update(irc, ice, '=');
}
break;
case '=':
switch(type)
{
case ICEP_VOICE: IRC_Printf(irc, sender, "Establishing voice call\n"); break;
case ICEP_QWSERVER: IRC_Printf(irc, sender, "Establishing game invite\n"); break;
case ICEP_QWCLIENT: IRC_Printf(irc, sender, "Establishing gatecrash\n"); break;
case ICEP_INVALID: break;
case ICEP_VIDEO: break;
}
IRC_ICE_Update(irc, ice, '*');
break;
default:
switch(type)
{
case ICEP_VOICE: IRC_Printf(irc, sender, "Updating voice call\n"); break;
case ICEP_QWSERVER: IRC_Printf(irc, sender, "Updating game invite\n"); break;
case ICEP_QWCLIENT: IRC_Printf(irc, sender, "Updating gatecrash\n"); break;
case ICEP_INVALID: break;
case ICEP_VIDEO: break;
}
IRC_ICE_Update(irc, ice, '%');
break;
}
}
}
else if (*token == '-')
{
IRC_Printf(irc, sender, "dropping connections is not supported yet: %s\n", token);
}
else
IRC_Printf(irc, sender, "ICE command type not supported\n", token);
}
void IRC_ICE_Frame(ircclient_t *irc)
{
char bah[8];
struct ircice_s *ice;
for (ice = irc->ice; ice; ice = ice->next)
{
if (!ice->accepted || !ice->allowed)
continue;
//ice needs some maintainence. if things change then we need to be prepared to send updated candidate info
piceapi->ICE_Get(ice->ice, "newlc", bah, sizeof(bah));
if (atoi(bah))
{
IRC_Printf(irc, ice->peer, "Sending updated peer info\n");
IRC_ICE_Update(irc, ice, '%');
}
//FIXME: detect when the ice connection goes idle.
}
}
static void IRC_ICE_Authorise(ircclient_t *irc, const char *with, enum iceproto_e type, qboolean authorize, char *announce)
{
struct ircice_s *ice, **link;
for (link = &irc->ice; *link; link = &(*link)->next)
{
ice = *link;
if (ice->type == type)
if (!strcmp(ice->peer, with))
{
if (authorize)
{
if (ice->allowed)
{
IRC_Printf(irc, announce, "Connection is already authorised\n");
return; //nothing to do
}
//yay! its good to go!
ice->allowed = true;
switch(type)
{
case ICEP_VOICE: IRC_Printf(irc, announce, "Accepting voice call\n"); break;
case ICEP_QWSERVER: IRC_Printf(irc, announce, "Accepting game invite\n"); break;
case ICEP_QWCLIENT: IRC_Printf(irc, announce, "Accepting gatecrash\n"); break;
case ICEP_VIDEO: break;
case ICEP_INVALID: break;
}
IRC_ICE_Update(irc, ice, '=');
}
else
{
IRC_ICE_Update(irc, ice, '-');
*link = ice->next;
if (ice->ice)
piceapi->ICE_Close(ice->ice);
IRC_Free(ice);
IRC_Printf(irc, announce, "Connection terminated\n");
}
return;
}
}
IRC_Printf(irc, announce, "Connection is already terminated\n");
}
qintptr_t IRC_ConsoleLink(qintptr_t *args)
{
ircclient_t *irc;
char link[256];
char *who = NULL;
char *channel = NULL;
char what[256];
char whobuf[256];
char which[512];
enum iceproto_e type;
// pCmd_Argv(0, text, sizeof(text));
pCmd_Argv(1, link, sizeof(link));
pCmd_Argv(2, which, sizeof(which));
Plug_Info_ValueForKey(link, "act", what, sizeof(what));
who = Plug_Info_ValueForKey(link, "who", whobuf, sizeof(whobuf));
for (irc = ircclients; irc; irc = irc->next)
{
if (!strncmp(irc->id, which, strlen(irc->id)))
{
channel = which + strlen(irc->id);
if (!*who)
who = channel;
break;
}
}
if (!irc || !who || !*what)
return false;
if (!strcmp(what, "reconnect"))
{
if (handleisvalid(irc->socket))
IRC_Printf(irc, channel, "Already %s.\n", irc->connecting?"reconnecting":"connected");
else if (IRC_Establish(irc))
IRC_Printf(irc, channel, "Reconnecting...\n");
else
IRC_Printf(irc, channel, "Unable to connect\n");
return true;
}
if (!*who)
return false; //that seems wrong. probably nothing to do with irc.
if (irc->tlsmode == TLS_STARTING || irc->connecting)
{
IRC_SetFooter(irc, channel, "Still connecting. Please wait.\n");
return true;
}
if (!strcmp(what, "iceaccept_v") || !strcmp(what, "iceaccept_s") || !strcmp(what, "iceaccept_c"))
{
switch(what[10])
{default:
case 'v': type = ICEP_VOICE; break;
case 's': type = ICEP_QWSERVER; break;
case 'c': type = ICEP_QWCLIENT; break;
}
IRC_Printf(irc, channel, "Accepting foo from %s\n", who);
IRC_ICE_Authorise(irc, who, type, true, channel);
return true;
}
else if (!strcmp(what, "icedecline_v") || !strcmp(what, "icedecline_s") || !strcmp(what, "icedecline_c"))
{
switch(what[11])
{default:
case 'v': type = ICEP_VOICE; break;
case 's': type = ICEP_QWSERVER; break;
case 'c': type = ICEP_QWCLIENT; break;
}
IRC_ICE_Authorise(irc, who, type, false, channel);
return true;
}
else if (!strcmp(what, "user"))
{
char links[2048];
char link[512];
Q_snprintf(links, sizeof(links), "%s:", who);
if (1)
{
Q_snprintf(link, sizeof(link), " ^[[Message]\\act\\msg\\who\\%s^]", who);
Q_strlcat(links, link, sizeof(links));
}
if (IRC_ICE_Find(irc, who, ICEP_VOICE))
{
Q_snprintf(link, sizeof(link), " ^[[Hang up]\\act\\icedecline_v\\who\\%s^]", who);
Q_strlcat(links, link, sizeof(links));
}
else
{
Q_snprintf(link, sizeof(link), " ^[[Call]\\act\\icestart_v\\who\\%s^]", who);
Q_strlcat(links, link, sizeof(links));
}
if (IRC_ICE_Find(irc, who, ICEP_QWSERVER))
{
Q_snprintf(link, sizeof(link), " ^[[Disconnect]\\act\\icedecline_s\\who\\%s^]", who);
Q_strlcat(links, link, sizeof(links));
}
else
{
Q_snprintf(link, sizeof(link), " ^[[Invite]\\act\\icestart_s\\who\\%s^]", who);
Q_strlcat(links, link, sizeof(links));
}
if (IRC_ICE_Find(irc, who, ICEP_QWCLIENT))
{
Q_snprintf(link, sizeof(link), " ^[[Disconnect]\\act\\icedecline_c\\who\\%s^]", who);
Q_strlcat(links, link, sizeof(links));
}
else
{
Q_snprintf(link, sizeof(link), " ^[[Gatecrash]\\act\\icestart_c\\who\\%s^]", who);
Q_strlcat(links, link, sizeof(links));
}
IRC_SetFooter(irc, channel, links);
return true;
}
else if (!strcmp(what, "msg"))
{
IRC_Printf(irc, who, "");
return true;
}
else if (!strcmp(what, "icestart_v") || !strcmp(what, "icestart_s") || !strcmp(what, "icestart_c"))
{
struct ircice_s *ice;
char *text;
char link[512];
switch(what[9])
{default:
case 'v': type = ICEP_VOICE; text = "Calling"; break;
case 's': type = ICEP_QWSERVER; text = "Inviting"; break;
case 'c': type = ICEP_QWCLIENT; text = "Gatecrashing"; break;
}
ice = IRC_ICE_Create(irc, who, type, true);
if (ice)
{
Q_snprintf(link, sizeof(link), "^["COLOURGREEN"%s\\act\\user^]", ice->peer);
IRC_ICE_Update(irc, ice, '+');
IRC_Printf(irc, ice->peer, "<%s %s ^[[Abort]\\act\\icedecline_%c\\who\\%s^]>\n", text, link, what[9], ice->peer);
}
return true;
}
return false;
}
//==================================================
int IRC_ClientFrame(ircclient_t *irc)
{
char prefix[64];
int ret;
char *nextmsg, *msg;
char *temp;
char token[1024];
char var[9][1000];
int i = 1;
ret = pNet_Recv(irc->socket, irc->bufferedinmessage+irc->bufferedinammount, sizeof(irc->bufferedinmessage)-1 - irc->bufferedinammount);
if (ret == 0)
{
if (!irc->bufferedinammount) //if we are half way through a message, read any possible conjunctions.
return IRC_DONE; //remove
}
if (ret < 0)
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;
strcpy(var[1],msg);
temp = strchr(var[1], ' ');
while (i < 8)
{
i++;
if (temp != NULL)
{
strcpy(var[i],temp+1);
}
else
{
strcpy(var[i], "");
}
temp=strchr(var[i], ' ');
}
IRC_CvarUpdate(); // is this the right place for it?
// if (irc_debug.value == 1) { IRC_Printf(irc, DEFAULTCONSOLE,COLOURRED "!!!!! ^11: %s ^22: %s ^33: %s ^44: %s ^55: %s ^66: %s ^77: %s ^88: %s\n",var[1],var[2],var[3],var[4],var[5],var[6],var[7],var[8]); }
if (irc_debug.value == 1) { IRC_Printf(irc, DEFAULTCONSOLE,COLOURRED "%s\n",var[1]); }
if (*msg == ':') //we need to strip off the prefix
{
char *sp = strchr(msg, ' ');
if (!sp)
{
IRC_Printf(irc, DEFAULTCONSOLE, "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_strlcpy(prefix, msg+1, sizeof(prefix));
else
Q_strlcpy(prefix, msg+1, sp-msg);
msg = sp;
while(*msg == ' ')
msg++;
}
else
strcpy(prefix, irc->server);
if (!strncmp(var[1], "NOTICE AUTH ", 12))
{
IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN "SERVER NOTICE: %s\n", var[3]+1);
}
else if (!strncmp(var[1], "PING ", 5))
{
IRC_AddClientMessage(irc, va("PONG %s", var[2]));
}
else if (!strncmp(var[2], "NOTICE ", 6))
{
char *exc = strchr(prefix, '!');
char *col = strchr(msg+6, ':');
char *end;
char *to = msg + 7;
char *etghack;
if (!strncmp(var[4]+1, "\1", 1))
{
char delimiters[] = "!";
char *username = strtok(var[1]+1, delimiters);
char *ctcpreplytype = strtok(var[4]+2, " ");
char *ctcpreply = var[5];
if (!strcmp(ctcpreplytype, "FTEICE"))
{
IRC_Printf(irc, username, "ICE from %s\n", username); //from client
IRC_ICE_Parse(irc, username, ctcpreply);
}
else
IRC_Printf(irc, DEFAULTCONSOLE,"<CTCP Reply> %s FROM %s: %s\n",ctcpreplytype,username,ctcpreply); // need to remove the last char on the end of ctcpreply
}
else if (exc && col)
{
*col = '\0';
col++;
while(*to <= ' ' && *to)
to++;
for (end = to + strlen(to)-1; end >= to && *end <= ' '; end--)
*end = '\0';
if (!strcmp(to, irc_nick.string))
to = prefix; //This was directed straight at us.
//So change the 'to', to the 'from'.
for (end = to; *end; end++)
{
if (*end >= 'A' && *end <= 'Z')
*end = *end + 'a' - 'A';
}
*exc = '\0';
if (!strncmp(col, "\001", 1))
{
end = strchr(col+1, '\001');
if (end)
*end = '\0';
if (!strncmp(col+1, "ACTION ", 7))
{
IRC_FilterMircColours(col+8);
IRC_Printf(irc, to, COLOURGREEN "***%s "COLORWHITE"%s\n", prefix, col+8); //from client
}
}
else
{
IRC_FilterMircColours(col);
IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN "NOTICE: -%s- %s\n", prefix, col); //from client
}
}
else
{
etghack = strtok(var[1],"\n");
if (!irc->connecting || IRC_WindowShown(irc, DEFAULTCONSOLE))
IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN "SERVER NOTICE: <%s> %s\n", prefix, etghack);
// strcpy(servername,prefix);
while (1)
{
etghack = strtok(NULL, "\n");
if (etghack == NULL)
break;
magic_etghack(etghack);
if (atoi(subvar[2]) != 0)
numbered_command(atoi(subvar[2]), etghack, irc);
else
IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN "SERVER NOTICE: <%s> %s\n", prefix, subvar[4]);
}
}
}
else if (!strncmp(var[2], "PRIVMSG ", 7)) //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;
char *to = msg + 7;
//message takes the form :FROM PRIVMSG TO :MESSAGE
if (BUILTINISVALID(LocalSound))
pLocalSound ("misc/talk.wav");
if ((!strcasecmp(var[4]+1, "\1VERSION\1")) && (!strncmp(var[2], "PRIVMSG ", 7)))
{
char *username;
char delimiters[] = "!";
username = strtok(var[1]+1, delimiters);
IRC_AddClientMessage(irc, va("NOTICE %s :\1VERSION FTEQW-IRC-Plugin Release: %s", username, RELEASE));
}
else if ((!strcasecmp(var[4]+1, "\1TIME\1")) && (!strncmp(var[2], "PRIVMSG ", 7)))
{
char delimiters[] = "!";
char *username = strtok(var[1], delimiters);
time_t t;
const struct tm *tm;
char buffer[100];
time(&t);
tm=localtime(&t);
strftime (buffer, 100, "%a %b %d %H:%M:%S", tm);
IRC_AddClientMessage(irc, va("NOTICE %s :\1TIME %s\1", username, buffer));
}
else if (exc && col)
{
char link[256];
*col = '\0';
col++;
while(*to <= ' ' && *to)
to++;
for (end = to + strlen(to)-1; end >= to && *end <= ' '; end--)
*end = '\0';
if (!strcmp(to, irc->nick))
to = prefix; //This was directed straight at us.
//So change the 'to', to the 'from'.
for (end = to; *end; end++)
{
if (*end >= 'A' && *end <= 'Z')
*end = *end + 'a' - 'A';
}
*exc = '\0';
//a link to interact with the sender
Q_snprintf(link, sizeof(link), "^["COLOURGREEN"%s\\act\\user\\who\\%s^]", prefix, prefix);
if (!strncmp(col, "\001", 1))
{
end = strchr(col+1, '\001');
if (end)
*end = '\0';
if (!strncmp(col+1, "ACTION ", 7))
{
IRC_FilterMircColours(col+8);
IRC_Printf(irc, to, "***%s %s\n", link, col+8); //from client
}
else if (!strncmp(col+1, "PING ", 5))
{
time_t currentseconds;
currentseconds = time (NULL);
IRC_Printf(irc, to, "CTCP Ping from %s\n", link); //from client
IRC_AddClientMessage(irc, va("NOTICE %s :\001PING %u\001\r\n", prefix, (unsigned int)currentseconds));
}
else if (!strncmp(col+1, "VERSION ", 8))
{
IRC_Printf(irc, to, "CTCP Version from %s\n", link); //from client
IRC_AddClientMessage(irc, va("NOTICE %s :\001VERSION "FULLENGINENAME" "RELEASE" \001\r\n", prefix));
}
else if (!strncmp(col+1, "FTEICE ", 7))
{
IRC_Printf(irc, to, "ICE from %s\n", link); //from client
IRC_ICE_Parse(irc, to, col+8);
}
else
{
if (end)//put it back on. might as well.
*end = '\001';
IRC_Printf(irc, to, "%s: %s\n", link, col); //from client
}
}
else
{
IRC_FilterMircColours(col);
IRC_Printf(irc, to, "%s: %s\n", link, col); //from client
}
}
else IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN "SERVER: <%s> %s\n", prefix, msg); //direct server message
}
else if (!strncmp(var[2], "MODE ", 5))
{
char *username = strtok(var[1]+1, "! ");
char *mode = strtok(var[4], " ");
char *target = strtok(var[5], " ");
char channel[100];
if (!strncmp(var[3], "#", 1))
{
strcpy(channel,strtok(var[3], " "));
}
else
{
strcpy(channel,DEFAULTCONSOLE);
}
if ((!strncmp(mode+1,"o", 1)) || (!strncmp(mode+1,"v",1))) // ops or voice
{
IRC_Printf(irc, channel,COLOURGREEN "%s sets mode %s on %s\n",username,mode,target);
}
else
{
if (IRC_WindowShown(irc, channel))
IRC_Printf(irc, channel, COLOURGREEN "%s sets mode %s\n",username,mode);
}
}
else if (!strncmp(var[2], "KICK ", 5))
{
char *username = strtok(var[1]+1, "!");
char *channel = strtok(var[3], " ");
char *target = strtok(var[4], " ");
char *reason = var[5]+1;
IRC_Printf(irc, channel,COLOURGREEN "%s was kicked from %s Reason: '%s' by %s\n",target,channel,reason,username);
}
else if (!strncmp(msg, "NICK ", 5))
{
char *exc = strchr(prefix, '!');
char *col = strchr(msg+5, ':');
if (exc && col)
{
*exc = '\0';
//fixme: print this in all channels as appropriate.
IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN "%s changes name to %s\n", prefix, col+1);
if (BUILTINISVALID(Con_RenameSub))
{
char oldname[256];
char newname[256];
Q_snprintf(oldname, sizeof(oldname), irc->id, prefix);
Q_snprintf(newname, sizeof(newname), irc->id, col+1);
pCon_RenameSub(oldname, newname); //if we were pming to them, rename accordingly.
}
}
else IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN ":%s%s\n", prefix, msg+6);
}
else if (!strncmp(msg, "PART ", 5))
{
char *exc = strchr(prefix, '!');
char *col = strchr(msg+5, ':');
if (exc && col)
{
*exc = '\0';
IRC_Printf(irc, msg+5, "%s leaves channel %s\n", prefix, col);
}
else IRC_Printf(irc, DEFAULTCONSOLE, 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';
IRC_Printf(irc, col+1, COLOURGREEN "%s joins channel %s\n", prefix, col+1);
}
else IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN ":%sJOIN %s\n", prefix, msg+5);
}
else if (!strncmp(msg, "372 ", 4))
{
char *text = strstr(msg, ":-");
if (!*irc->autochannels || irc_motd.value)
{
if (text)
IRC_Printf(irc, DEFAULTCONSOLE, "%s\n", text+2);
else
IRC_Printf(irc, DEFAULTCONSOLE, "%s\n", msg);
}
}
else if (!strncmp(msg, "331 ", 4) ||//no topic
!strncmp(msg, "332 ", 4)) //the topic
{
char *topic;
char *chan;
topic = COM_Parse(msg, token, sizeof(token));
topic = COM_Parse(topic, token, sizeof(token));
topic = COM_Parse(topic, token, sizeof(token));
while(*topic == ' ')
topic++;
if (*topic == ':')
{
topic++;
chan = token;
}
else
{
topic = "No topic";
chan = DEFAULTCONSOLE;
}
IRC_Printf(irc, chan, "Topic on channel %s is: "COLOURGREEN"%s\n", chan, topic);
}
else if (!strncmp(msg, "353 ", 4)) //the names of people on a channel
{
char *eq = strstr(msg, "="); // BAD SPIKE!! = is normal channel :(
char *eq2 = strstr(msg, "@"); // @ means the channel is +s (secret)
char *eq3 = strstr(msg, "*"); // * means the channel is +p (private) rather redundant...
char *channeltype = strtok(var[4], " ");
char *channel = strtok(var[5], " ");
char *str;
int secret = 0;
int privatechan = 0;
if ( !strcmp(channeltype,"=") )
{
char *end;
eq++;
str = strstr(eq, ":");
while(*eq == ' ')
eq++;
for (end = eq; *end>' '&&*end !=':'; end++)
;
*end = '\0';
str++;
}
//else if (eq2)
else if ( !strcmp(channeltype,"@") )
{
char *end;
secret = 1;
eq2++;
str = strstr(eq2, ":");
while(*eq2 == ' ')
eq2++;
for (end = eq2; *end>' '&&*end !=':'; end++)
;
*end = '\0';
str++;
}
else if ( !strcmp(channeltype,"*") )
{
char *end;
privatechan = 1;
eq3++;
str = strstr(eq3, ":");
while(*eq3 == ' ')
eq3++;
for (end = eq3; *end>' '&&*end !=':'; end++)
;
*end = '\0';
str++;
}
else
{
eq = "Corrupted_Message";
str = NULL;
}
IRC_Printf(irc, channel, va("Users on channel %s:\n", channel));
while (str)
{
str = COM_Parse(str, token, sizeof(token));
if (*token == '@') //they're an operator
IRC_Printf(irc, channel, "^[@"COLOURGREEN"%s\\act\\user\\who\\%s\\tip\\Channel Operator^]\n", token+1, token+1);
else if (*token == '%') //they've got half-op
IRC_Printf(irc, channel, "^[%%"COLOURGREEN"%s\\act\\user\\who\\%s\\tip\\Channel Half-Operator^]\n", token+1, token+1);
else if (*token == '+') //they've got voice
IRC_Printf(irc, channel, "^[+"COLOURGREEN"%s\\act\\user\\who\\%s\\tip\\Voice^]\n", token+1, token+1);
else
IRC_Printf(irc, channel, " ^["COLOURGREEN"%s\\act\\user\\who\\%s^]\n", token, token);
}
if (secret == 1)
{
IRC_Printf(irc, channel, "%s is secret (+s)\n",channel);
}
else if (privatechan == 1)
{
IRC_Printf(irc, channel, "%s is private (+p)\n",channel);
}
}
// would be great to convert the above to work better
else if (atoi(var[2]) != 0)
{
// char *rawparameter = strtok(var[4], " ");
// char *rawmessage = var[5];
// char *wholerawmessage = var[4];
numbered_command(atoi(var[2]), msg, irc);
IRC_CvarUpdate();
if (irc_debug.value == 1) { IRC_Printf(irc, DEFAULTCONSOLE, "%s\n", msg); }
}
else
IRC_Printf(irc, DEFAULTCONSOLE, "%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.
qintptr_t IRC_Frame(qintptr_t *args)
{
ircclient_t *ircclient;
if (reloadconfig)
{
reloadconfig = false;
IRC_ParseConfig();
}
for (ircclient = ircclients; ircclient; ircclient = ircclient->next)
{
int stat = IRC_CONTINUE;
if (!handleisvalid(ircclient->socket))
continue; //this connection isn't enabled.
while(stat == IRC_CONTINUE)
{
stat = IRC_ClientFrame(ircclient);
if (ircclient->bufferedoutammount)
{
int flushed = pNet_Send(ircclient->socket, ircclient->bufferedoutmessage, ircclient->bufferedoutammount); //FIXME: This needs rewriting to cope with errors+throttle.
if (flushed > 0)
{
memmove(ircclient->bufferedoutmessage, ircclient->bufferedoutmessage+flushed, ircclient->bufferedoutammount - flushed);
ircclient->bufferedoutammount -= flushed;
}
}
}
if (ircclient->quitting && !ircclient->bufferedoutammount)
stat = IRC_KILL;
if (stat == IRC_KILL)
{
pNet_Close(ircclient->socket);
ircclient->socket = invalid_handle;
IRC_Printf(ircclient, DEFAULTCONSOLE, "Disconnected from irc\n^[[Reconnect]\\act\\reconnect^]\n");
break; //lazy
}
else
IRC_ICE_Frame(ircclient);
}
return 0;
}
void IRC_Command(ircclient_t *ircclient, char *dest)
{
char token[1024];
char imsg[8192];
char *msg;
pCmd_Args(imsg, sizeof(imsg));
msg = COM_Parse(imsg, token, sizeof(token));
if (*token == '/')
{
if (!strcmp(token+1, "open") || !strcmp(token+1, "connect"))
{
char server[256];
char channels[1024];
char nick[256];
char password[256];
msg = COM_Parse(msg, server, sizeof(server));
msg = COM_Parse(msg, channels, sizeof(channels));
msg = COM_Parse(msg, nick, sizeof(nick));
msg = COM_Parse(msg, password, sizeof(password));
//set up some defaults
if (!*nick)
Q_strlcpy(nick, irc_nick.string, sizeof(nick));
if (!*nick)
pCvar_GetString("name", nick, sizeof(nick));
if (IRC_FindAccount(server))
{
IRC_Printf(ircclient, dest, "IRC connection to %s already registered\n");
return; //silently ignore it if the account already exists
}
ircclient = IRC_Create(server, nick, defaultuser, irc_hostname.string, password, channels);
if (ircclient)
{
if (IRC_Establish(ircclient))
IRC_Printf(ircclient, dest, "Trying to connect\n");
else
IRC_Printf(ircclient, dest, "Unable to connect\n");
}
else
IRC_Printf(ircclient, dest, "Unable to open account\n");
IRC_WriteConfig();
}
else if (!strcmp(token+1, "help"))
{
IRC_Printf(ircclient, dest, "to connect to a server: /connect SERVER \"#chan #chan2,chan2password\" NICK SERVERPASSWORD\n");
IRC_Printf(ircclient, dest, "to disconnect from a server: /quit\n");
IRC_Printf(ircclient, dest, "to join a channel: /join\n");
IRC_Printf(ircclient, dest, "to leave a channel: /part\n");
IRC_Printf(ircclient, dest, "note that servers and channels will be remembered\n");
}
else if (!strcmp(token+1, "nick"))
{
msg = COM_Parse(msg, token, sizeof(token));
if (!ircclient) //not yet connected.
pCvar_SetString(irc_nick.name, token);
else
IRC_SetNick(ircclient, token);
IRC_WriteConfig();
}
else if (!strcmp(token+1, "user"))
{
msg = COM_Parse(msg, token, sizeof(token));
Q_strlcpy(defaultuser, token, sizeof(defaultuser));
if (ircclient)
IRC_SetUser(ircclient, defaultuser);
IRC_WriteConfig();
}
else if (!strcmp(token+1, "info") || !strcmp(token+1, "status"))
{
ircclient_t *e;
struct ircice_s *ice;
for (e = ircclients; e; e = e->next)
{
IRC_Printf(ircclient, dest, "SERVER: %s\n", e->server);
if (e->connecting && handleisvalid(e->socket))
IRC_Printf(ircclient, dest, "<CONNECTING>\n");
else if (handleisvalid(e->socket))
IRC_Printf(ircclient, dest, "<CONNECTED>\n");
else
IRC_Printf(ircclient, dest, "<DISCONNECTED>\n");
if (e->quitting)
IRC_Printf(ircclient, dest, "<QUITTING>\n");
switch(e->tlsmode)
{
default:
case TLS_OFF:
IRC_Printf(ircclient, dest, "TLS: insecure\n");
break;
case TLS_INITIAL:
IRC_Printf(ircclient, dest, "TLS: initial\n");
break;
case TLS_START:
case TLS_STARTING:
IRC_Printf(ircclient, dest, "TLS: upgrade\n");
break;
}
IRC_Printf(ircclient, dest, "nick: %s\n", e->nick);
IRC_Printf(ircclient, dest, "realname: %s\n", e->realname);
IRC_Printf(ircclient, dest, "hostname: %s\n", e->hostname);
IRC_Printf(ircclient, dest, "rejoin: %s\n", *e->autochannels?e->autochannels:"<no channels>");
IRC_Printf(ircclient, dest, "default dest: %s\n", e->defaultdest);
for (ice = e->ice; ice; ice = ice->next)
{
char *allowed=ice->allowed?" allowed":" not-allowed";
char *accepted=ice->accepted?" accepted":" not-accepted";
switch(ice->type)
{
default:
case ICEP_INVALID:
IRC_Printf(ircclient, dest, " <INVALID ICE>\n");
break;
case ICEP_QWSERVER:
IRC_Printf(ircclient, dest, " server: \"%s\"%s%s\n", ice->peer, allowed, accepted);
break;
case ICEP_QWCLIENT:
IRC_Printf(ircclient, dest, " client: \"%s\"%s%s\n", ice->peer, allowed, accepted);
break;
case ICEP_VOICE:
IRC_Printf(ircclient, dest, " voice: \"%s\"%s%s\n", ice->peer, allowed, accepted);
break;
case ICEP_VIDEO:
IRC_Printf(ircclient, dest, " voice: \"%s\"%s%s\n", ice->peer, allowed, accepted);
break;
}
}
}
}
else if (!ircclient)
{
IRC_Printf(ircclient, dest, "Not connected, please connect to an irc server first.\n");
}
//ALL other commands require you to be connected.
else if (!strcmp(token+1, "list"))
{
IRC_AddClientMessage(ircclient, "LIST");
}
else if ( !strcmp(token+1, "join") || !strcmp(token+1, "j") )
{
char chan[256];
char pwd[256];
if (ircclient->tlsmode == TLS_STARTING || ircclient->connecting)
{
IRC_Printf(ircclient, dest, "Still connecting. Please wait.\n");
return;
}
msg = COM_Parse(msg, chan, sizeof(chan));
msg = COM_Parse(msg, pwd, sizeof(pwd));
IRC_JoinChannel(ircclient,chan,pwd);
IRC_WriteConfig();
}
else if (!strcmp(token+1, "part") || !strcmp(token+1, "leave")) // need to implement leave reason
{
msg = COM_Parse(msg, token, sizeof(token));
IRC_PartChannel(ircclient, *token?token:dest);
IRC_WriteConfig();
}
else if (!strcmp(token+1, "call"))
{
msg = COM_Parse(msg, token, sizeof(token));
if (ircclient->tlsmode == TLS_STARTING || ircclient->connecting)
{
IRC_Printf(ircclient, dest, "Still connecting. Please wait.\n");
return;
}
{
struct ircice_s *ice = IRC_ICE_Create(ircclient, *token?token:dest, ICEP_VOICE, true);
if (ice)
{
IRC_ICE_Update(ircclient, ice, '+');
IRC_Printf(ircclient, ice->peer, "<Calling %s>\n", ice->peer);
}
}
}
else if (!strcmp(token+1, "ginvite"))
{
msg = COM_Parse(msg, token, sizeof(token));
if (ircclient->tlsmode == TLS_STARTING || ircclient->connecting)
{
IRC_Printf(ircclient, dest, "Still connecting. Please wait.\n");
return;
}
{
struct ircice_s *ice = IRC_ICE_Create(ircclient, *token?token:dest, ICEP_QWSERVER, true);
if (ice)
{
IRC_ICE_Update(ircclient, ice, '+');
IRC_Printf(ircclient, ice->peer, "<inviting %s>\n", ice->peer);
}
}
}
else if (!strcmp(token+1, "gatecrash"))
{
msg = COM_Parse(msg, token, sizeof(token));
if (ircclient->tlsmode == TLS_STARTING || ircclient->connecting)
{
IRC_Printf(ircclient, dest, "Still connecting. Please wait.\n");
return;
}
{
struct ircice_s *ice = IRC_ICE_Create(ircclient, *token?token:dest, ICEP_QWCLIENT, true);
if (ice)
{
IRC_ICE_Update(ircclient, ice, '+');
IRC_Printf(ircclient, ice->peer, "<gatecrashing %s>\n", ice->peer);
}
}
}
else if (!strcmp(token+1, "msg"))
{
msg = COM_Parse(msg, token, sizeof(token));
if (!msg)
return;
if (ircclient->tlsmode == TLS_STARTING || ircclient->connecting)
{
IRC_Printf(ircclient, dest, "Still connecting. Please wait.\n");
return;
}
IRC_AddClientMessage(ircclient, va("PRIVMSG %s :%s", token, msg+1));
IRC_Printf(ircclient, token, "%s: %s\n", ircclient->nick, msg);
}
else if (!strcmp(token+1, "quote") || !strcmp(token+1, "raw"))
{
IRC_AddClientMessage(ircclient, va("%s", msg));
}
else if (!strcmp(token+1, "reconnect"))
{
if (IRC_Establish(ircclient))
IRC_Printf(ircclient, dest, "Trying to connect\n");
else
IRC_Printf(ircclient, dest, "Unable to connect\n");
}
else if (!strcmp(token+1, "quit") || !strcmp(token+1, "disconnect"))
{
msg = COM_Parse(msg, token, sizeof(token));
if (*token)
IRC_AddClientMessage(ircclient, va("QUIT :%s", token));
else
IRC_AddClientMessage(ircclient, va("QUIT :%s", irc_quitmessage.string));
ircclient->quitting = true;
IRC_WriteConfig();
}
else if (!strcmp(token+1, "whois"))
{
msg = COM_Parse(msg, token, sizeof(token));
IRC_AddClientMessage(ircclient, va("WHOIS :%s",token));
}
else if (!strcmp(token+1, "away"))
{
if ( strlen(msg) > 1 )
IRC_AddClientMessage(ircclient, va("AWAY :%s",msg+1));
else
IRC_AddClientMessage(ircclient, va("AWAY :"));
}
else if (!strcmp(token+1, "motd"))
{
IRC_AddClientMessage(ircclient, "MOTD");
}
else if (!strcmp(token+1, "ctcp"))
{
msg = COM_Parse(msg, token, sizeof(token));
IRC_AddClientMessage(ircclient, va("PRIVMSG %s :\1%s\1",token,msg+1));
}
else if (!strcmp(token+1, "dest"))
{
msg = COM_Parse(msg, token, sizeof(token));
Q_strlcpy(ircclient->defaultdest, token, sizeof(ircclient->defaultdest));
}
else if (!strcmp(token+1, "ping"))
{
if (!*dest)
IRC_Printf(ircclient, DEFAULTCONSOLE, "No channel joined. Try /join #<channel>\n");
else
IRC_AddClientMessage(ircclient, va("PRIVMSG %s :\001PING%s\001", dest, msg));
}
else if (!strcmp(token+1, "notice"))
{
msg = COM_Parse(msg, token, sizeof(token));
IRC_AddClientMessage(ircclient, va("NOTICE %s :%s",token, msg+1));
}
else if (!strcmp(token+1, "me"))
{
if (!*dest)
IRC_Printf(ircclient, DEFAULTCONSOLE, "No channel joined. Try /join #<channel>\n");
else
{
if(*msg <= ' ' && *msg)
msg++;
IRC_AddClientMessage(ircclient, va("PRIVMSG %s :\001ACTION %s\001", dest, msg));
IRC_Printf(ircclient, ircclient->defaultdest, "***^3%s^7 %s\n", ircclient->nick, msg);
}
}
}
else
{
if (ircclient)
{
if (!handleisvalid(ircclient->socket))
IRC_Printf(ircclient, dest, "Connection was closed. use /reconnect\n");
else if (ircclient->tlsmode == TLS_STARTING || ircclient->connecting)
IRC_Printf(ircclient, dest, "Still connecting. Please wait.\n");
else if (!*dest)
{
IRC_Printf(ircclient, dest, "No channel joined. Try /join #<channel>\n");
}
else
{
msg = imsg;
while (*msg == ' ')
msg++;
if (!*msg)
return; //this is apparently an error. certainly wasteful.
IRC_AddClientMessage(ircclient, va("PRIVMSG %s :%s", dest, msg));
IRC_Printf(ircclient, dest, "^3%s^7: %s\n", ircclient->nick, msg);
}
}
else
IRC_Printf(ircclient, "Not connected\ntype \"%s /open IRCSERVER [#channel1[,#channel2[,...]]] [nick]\" to connect\n", COMMANDNAME);
}
}