mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-29 23:22:01 +00:00
fb239abaf7
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6009 fc73d0e0-1445-4013-8a0c-d673dee63da5
2821 lines
74 KiB
C
2821 lines
74 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"
|
|
static plugsubconsolefuncs_t *confuncs;
|
|
static plugfsfuncs_t *filefuncs;
|
|
static plugnetfuncs_t *netfuncs;
|
|
static plug2dfuncs_t *drawfuncs;
|
|
#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"
|
|
static vmcvar_t irc_debug = {"irc_debug", "0", irccvars, 0};
|
|
static vmcvar_t irc_motd = {"irc_motd", "0", irccvars, 0};
|
|
static vmcvar_t irc_nick = {"irc_nick", "", irccvars, 0};
|
|
static vmcvar_t irc_altnick = {"irc_altnick", "", irccvars, 0};
|
|
static vmcvar_t irc_realname = {"irc_realname", "FTE IRC-Plugin", irccvars, 0};
|
|
static vmcvar_t irc_hostname = {"irc_hostname", "localhost", irccvars, 0};
|
|
static vmcvar_t irc_username = {"irc_username", "FTE", irccvars, 0};
|
|
static vmcvar_t irc_timestamp = {"irc_timestamp", "0", irccvars, 0};
|
|
static vmcvar_t irc_quitmessage = {"irc_quitmessage", "", irccvars, 0};
|
|
static vmcvar_t irc_config = {"irc_config", "1", irccvars, 0};
|
|
#undef irccvars
|
|
|
|
static vmcvar_t *cvarlist[] ={
|
|
&irc_debug,
|
|
&irc_motd,
|
|
&irc_nick,
|
|
&irc_altnick,
|
|
&irc_realname,
|
|
&irc_hostname,
|
|
&irc_username,
|
|
&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"
|
|
|
|
#if defined(SVNREVISION)
|
|
#define RELEASE STRINGIFY(SVNREVISION)
|
|
#else
|
|
#define RELEASE __DATE__
|
|
#endif
|
|
|
|
static struct
|
|
{
|
|
int width;
|
|
int height;
|
|
} pvid;
|
|
static void QDECL IRC_UpdateVideo(int width, int height)
|
|
{
|
|
pvid.width = width;
|
|
pvid.height = height;
|
|
}
|
|
|
|
static qboolean (*Con_TrySubPrint)(const char *subname, const char *text);
|
|
static qboolean Con_FakeSubPrint(const char *subname, const char *text)
|
|
{
|
|
plugfuncs->Print(text);
|
|
return true;
|
|
}
|
|
|
|
//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)
|
|
|
|
|
|
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]; //nick that we're actually using
|
|
size_t nicktries; //so we can cycle nicks till we get one that works.
|
|
|
|
qboolean persist; //server connection is persistent across restarts
|
|
char primarynick[IRC_MAXNICKLEN]; //primary nick the connection was configured with
|
|
char pwd[128]; //server password
|
|
char realname[128]; //this is your descriptive OS user account... supposedly.
|
|
char username[128]; //this is your unique OS user name... supposedly.
|
|
char hostname[128]; //this is your OS hostname... supposedly.
|
|
char autochannels[256]; //"#chan,pwd #foo,bar #fred #splodge" for four channnels, two with a password
|
|
|
|
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;
|
|
static ircclient_t *ircclients;
|
|
|
|
|
|
static 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 (confuncs && confuncs->GetConsoleFloat(lwr, "iswindow") < true)
|
|
{
|
|
confuncs->SetConsoleString(lwr, "title", *channame?channame:irc->server);
|
|
confuncs->SetConsoleString(lwr, "prompt", va("[^1%s^7]: ", irc->nick));
|
|
confuncs->SetConsoleFloat(lwr, "iswindow", 2);
|
|
confuncs->SetConsoleFloat(lwr, "forceutf8", true);
|
|
confuncs->SetConsoleFloat(lwr, "wnd_w", 256);
|
|
confuncs->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;
|
|
}
|
|
confuncs->SetConsoleFloat(lwr, "wnd_x", next_window_x);
|
|
confuncs->SetConsoleFloat(lwr, "wnd_y", next_window_y);
|
|
next_window_x += 256;
|
|
}
|
|
|
|
if (confuncs)
|
|
confuncs->SetConsoleString(lwr, "footer", string);
|
|
}
|
|
}
|
|
static 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 (confuncs && confuncs->GetConsoleFloat(lwr, "iswindow") < true)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
static void IRC_Printf(ircclient_t *irc, const char *subname, const char *format, ...) LIKEPRINTF(3);
|
|
static 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)
|
|
plugfuncs->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 (confuncs && confuncs->GetConsoleFloat(lwr, "iswindow") < true)
|
|
{
|
|
confuncs->SetConsoleString(lwr, "title", *channame?channame:irc->server);
|
|
confuncs->SetConsoleString(lwr, "prompt", va("[^1%s^7]: ", irc->nick));
|
|
confuncs->SetConsoleFloat(lwr, "iswindow", 2);
|
|
confuncs->SetConsoleFloat(lwr, "forceutf8", true);
|
|
confuncs->SetConsoleFloat(lwr, "wnd_w", 256);
|
|
confuncs->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;
|
|
}
|
|
confuncs->SetConsoleFloat(lwr, "wnd_x", next_window_x);
|
|
confuncs->SetConsoleFloat(lwr, "wnd_y", next_window_y);
|
|
next_window_x += 256;
|
|
}
|
|
if (!*string)
|
|
confuncs->SetActive(lwr);
|
|
|
|
Con_TrySubPrint(lwr, string);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
static void IRC_InitCvars(void)
|
|
{
|
|
vmcvar_t *v;
|
|
int i;
|
|
for (i=0; cvarlist[i]; i++)
|
|
{
|
|
v = cvarlist[i];
|
|
v->handle = cvarfuncs->Register(v->name, v->string, v->flags, v->group);
|
|
}
|
|
}
|
|
|
|
static int IRC_CvarUpdate(void) // perhaps void instead?
|
|
{
|
|
vmcvar_t *v;
|
|
int i;
|
|
for (i=0; cvarlist[i]; i++)
|
|
{
|
|
v = cvarlist[i];
|
|
cvarfuncs->Update(v->handle, &v->modificationcount, v->string, sizeof(v->string), &v->value);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void IRC_Command(ircclient_t *ircclient, char *dest, char *args);
|
|
qboolean IRC_ExecuteCommand(qboolean isinsecure);
|
|
int IRC_ConExecuteCommand(qboolean isinsecure);
|
|
void IRC_Frame(double realtime, double gametime);
|
|
qboolean IRC_ConsoleLink(void);
|
|
|
|
qboolean Plug_Init(void)
|
|
{
|
|
confuncs = plugfuncs->GetEngineInterface(plugsubconsolefuncs_name, sizeof(*confuncs));
|
|
filefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));
|
|
netfuncs = plugfuncs->GetEngineInterface(plugnetfuncs_name, sizeof(*netfuncs));
|
|
drawfuncs = plugfuncs->GetEngineInterface(plug2dfuncs_name, sizeof(*drawfuncs));
|
|
piceapi = plugfuncs->GetEngineInterface(ICE_API_CURRENT, sizeof(*piceapi));
|
|
plugfuncs->ExportFunction("UpdateVideo", IRC_UpdateVideo);
|
|
|
|
if (netfuncs &&
|
|
filefuncs &&
|
|
plugfuncs->ExportFunction("Tick", IRC_Frame) &&
|
|
plugfuncs->ExportFunction("ExecuteCommand", IRC_ExecuteCommand))
|
|
{
|
|
cmdfuncs->AddCommand(COMMANDNAME);
|
|
|
|
plugfuncs->ExportFunction("ConsoleLink", IRC_ConsoleLink);
|
|
if (!confuncs || !plugfuncs->ExportFunction("ConExecuteCommand", IRC_ConExecuteCommand))
|
|
Con_TrySubPrint = Con_FakeSubPrint;
|
|
else
|
|
Con_TrySubPrint = confuncs->SubPrint;
|
|
|
|
reloadconfig = true;
|
|
IRC_InitCvars();
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
plugfuncs->Print("IRC Client Plugin failed\n");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
qboolean IRC_ExecuteCommand(qboolean isinsecure)
|
|
{
|
|
char cmd[256];
|
|
cmdfuncs->Argv(0, cmd, sizeof(cmd));
|
|
if (!strcmp(cmd, COMMANDNAME))
|
|
{
|
|
ircclient_t *ircclient = ircclients;
|
|
char imsg[8192];
|
|
cmdfuncs->Args(imsg, sizeof(imsg));
|
|
//FIXME: select an irc network more inteligently
|
|
IRC_Command(ircclient, ircclient?ircclient->defaultdest:"", imsg);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
int IRC_ConExecuteCommand(qboolean isinsecure)
|
|
{
|
|
char buffer[256];
|
|
char imsg[8192];
|
|
ircclient_t *ircclient;
|
|
//FIXME: select the right network
|
|
|
|
cmdfuncs->Argv(0, buffer, sizeof(buffer));
|
|
cmdfuncs->Args(imsg, sizeof(imsg));
|
|
|
|
//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, "", imsg);
|
|
else
|
|
Con_TrySubPrint(buffer, "You were disconnected\n");
|
|
return true;
|
|
}
|
|
|
|
IRC_Command(ircclient, buffer+strlen(ircclient->id), imsg);
|
|
return true;
|
|
}
|
|
|
|
static 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); }
|
|
}
|
|
|
|
static 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
|
|
}
|
|
|
|
static ircclient_t *IRC_Create(const char *server, const char *nick, const char *realname, const char *hostname, const char *username, 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();
|
|
|
|
Q_strlcpy(irc->primarynick, nick, sizeof(irc->primarynick));
|
|
Q_strlcpy(irc->nick, nick, sizeof(irc->nick));
|
|
Q_strlcpy(irc->realname, realname, sizeof(irc->realname));
|
|
Q_strlcpy(irc->hostname, hostname, sizeof(irc->hostname));
|
|
Q_strlcpy(irc->username, username, sizeof(irc->username));
|
|
Q_strlcpy(irc->pwd, password, sizeof(irc->pwd));
|
|
|
|
Q_strlcpy(irc->autochannels,channels, sizeof(irc->autochannels));
|
|
|
|
// gethostname(irc->hostname, sizeof(irc->hostname));
|
|
// irc->hostname[sizeof(irc->hostname)-1] = 0;
|
|
|
|
irc->next = ircclients;
|
|
ircclients = irc;
|
|
|
|
return irc;
|
|
}
|
|
|
|
static void IRC_SetPass(ircclient_t *irc, char *pass)
|
|
{
|
|
if (irc->pwd != pass)
|
|
Q_strlcpy(irc->pwd, pass, sizeof(irc->pwd));
|
|
if (*pass && irc->tlsmode != TLS_STARTING)
|
|
IRC_AddClientMessage(irc, va("PASS %s", pass));
|
|
}
|
|
static void IRC_SetNick(ircclient_t *irc, char *nick)
|
|
{
|
|
if (irc->nick != nick)
|
|
Q_strlcpy(irc->nick, nick, sizeof(irc->nick));
|
|
if (irc->tlsmode != TLS_STARTING)
|
|
IRC_AddClientMessage(irc, va("NICK %s", irc->nick));
|
|
}
|
|
static void IRC_SetUser(ircclient_t *irc, char *user)
|
|
{
|
|
IRC_CvarUpdate();
|
|
|
|
if (irc->tlsmode != TLS_STARTING)
|
|
{
|
|
const char *username = irc->username;
|
|
const char *realname = irc->realname;
|
|
if (!*username)
|
|
username = getenv("USER");
|
|
if (!username)
|
|
username = "FTE"; //we need something.
|
|
|
|
if (!*realname)
|
|
realname = username;
|
|
//servers will usually ignore the server arg, as they usually know their own dns name already...
|
|
//servers SHOULD ignore the hostname arg too (using a reverse dns). or they'll just replace it with an ip address (note: could use this instead of a STUN server).
|
|
//the username+realname are used, and need to be persistent for auto-op type mechanisms.
|
|
IRC_AddClientMessage(irc, va("USER %s %s %s :%s", username, irc->hostname, irc->server, realname));
|
|
}
|
|
}
|
|
|
|
static 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 = netfuncs->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 (netfuncs->SetTLSClient(irc->socket, irc->server) < 0)
|
|
{
|
|
netfuncs->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->nicktries = 0;
|
|
IRC_SetPass(irc, irc->pwd);
|
|
IRC_SetNick(irc, irc->nick);
|
|
IRC_SetUser(irc, irc_username.string);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void IRC_ParseConfig(void)
|
|
{
|
|
qhandle_t config;
|
|
int len = filefuncs->Open("**plugconfig", &config, 1);
|
|
if (len >= 0)
|
|
{
|
|
char *buf = malloc(len+1);
|
|
char *msg = buf;
|
|
buf[len] = 0;
|
|
filefuncs->Read(config, buf, len);
|
|
filefuncs->Close(config);
|
|
|
|
while (msg && *msg)
|
|
{
|
|
ircclient_t *irc;
|
|
char server[256];
|
|
char channels[1024];
|
|
char nick[256];
|
|
char password[256];
|
|
char realname[256];
|
|
char hostname[256];
|
|
char username[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, realname, sizeof(realname));
|
|
msg = COM_Parse(msg, hostname, sizeof(hostname));
|
|
msg = COM_Parse(msg, username, sizeof(username));
|
|
if (*server)
|
|
{
|
|
irc = IRC_Create(server, nick, realname, hostname, username, password, channels);
|
|
if (irc)
|
|
{
|
|
irc->persist = true;
|
|
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);
|
|
}
|
|
}
|
|
static void IRC_WriteConfig(void)
|
|
{
|
|
qhandle_t config;
|
|
|
|
if (irc_config.value == 0)
|
|
return;
|
|
|
|
filefuncs->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\" \"%s\"\n", irc->server, irc->autochannels, irc->primarynick, irc->pwd, irc->realname, irc->hostname, irc->username);
|
|
if (irc->quitting || !irc->persist)
|
|
continue;
|
|
filefuncs->Write(config, s, strlen(s));
|
|
}
|
|
|
|
filefuncs->Close(config);
|
|
}
|
|
}
|
|
static void IRC_MakeDefault(ircclient_t *irc)
|
|
{ //unlinks the client, then links it at the head, so that its the first found (thus the default)
|
|
ircclient_t **link;
|
|
|
|
for (link = &ircclients; *link; link = &(*link)->next)
|
|
{
|
|
if (*link == irc)
|
|
{
|
|
*link = irc->next;
|
|
break;
|
|
}
|
|
}
|
|
|
|
irc->next = ircclients;
|
|
ircclients = irc;
|
|
}
|
|
|
|
static 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, " ");
|
|
}
|
|
}
|
|
|
|
static void IRC_PartChannel(ircclient_t *irc, char *channelname)
|
|
{
|
|
IRC_PartChannelInternal(irc, channelname);
|
|
IRC_AddClientMessage(irc, va("PART %s", channelname));
|
|
}
|
|
|
|
static 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));
|
|
}
|
|
}
|
|
|
|
static 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
|
|
|
|
*/
|
|
static 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
|
|
|
|
static 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], ' ');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static 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], ' ');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void IRC_TryNewNick(ircclient_t *irc, char *nickname)
|
|
{
|
|
char *seedednick;
|
|
|
|
IRC_CvarUpdate();
|
|
|
|
if (irc->tlsmode == TLS_STARTING)
|
|
{
|
|
//don't submit any of this info here.
|
|
return;
|
|
}
|
|
if (irc->connecting)
|
|
{
|
|
if (irc->nicktries == 0)
|
|
{
|
|
irc->nicktries++;
|
|
if (*irc->primarynick && strcmp(nickname, irc->primarynick))
|
|
{
|
|
IRC_SetNick(irc, irc->primarynick);
|
|
return;
|
|
}
|
|
}
|
|
if (irc->nicktries == 1)
|
|
{
|
|
irc->nicktries++;
|
|
if (*irc_nick.string && strcmp(nickname, irc_nick.string))
|
|
{
|
|
IRC_SetNick(irc, irc_nick.string);
|
|
return;
|
|
}
|
|
}
|
|
if (irc->nicktries == 2)
|
|
{
|
|
irc->nicktries++;
|
|
if (*irc_altnick.string && strcmp(nickname, irc_altnick.string))
|
|
{
|
|
IRC_SetNick(irc, irc_altnick.string);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (++irc->nicktries == 10)
|
|
{
|
|
IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "ERROR: Unable to obtain usable nickname\n");
|
|
return;
|
|
}
|
|
|
|
//panic and pick something at random
|
|
//IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "ERROR: primary nickname in use. Attempting random nickname.\n");
|
|
if (*irc->primarynick && irc->nicktries < 7)
|
|
seedednick = va("%.6s%i", irc->primarynick, rand());
|
|
else if (*irc_nick.string && irc->nicktries < 8)
|
|
seedednick = va("%.6s%i", irc_nick.string, rand());
|
|
else if (*irc_altnick.string && irc->nicktries < 9)
|
|
seedednick = va("%.6s%i", irc_altnick.string, rand());
|
|
else
|
|
seedednick = va("%.6s%i", "FTE", rand());
|
|
seedednick[9] = 0; //'Each client is distinguished from other clients by a unique nickname having a maximum length of nine (9) characters'
|
|
|
|
IRC_SetNick(irc, seedednick);
|
|
}
|
|
}
|
|
|
|
//==================================================
|
|
|
|
static 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 333: /* RPL_TOPICWHOTIME channel user timestamp*/
|
|
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 */
|
|
case 442: /* ERR_NOTONCHANNEL */
|
|
{
|
|
char *username = strtok(casevar[3], " ");
|
|
char *error = casevar[4]+1;
|
|
|
|
IRC_Printf(irc, username, COLOURRED "ERROR <%s>: %s\n",username,error);
|
|
return;
|
|
}
|
|
case 432: /* #define ERR_ERRONEUSNICKNAME 432 */
|
|
{
|
|
IRC_Printf(irc, DEFAULTCONSOLE, "Erroneous/invalid nickname given\n");
|
|
IRC_TryNewNick(irc, "FTEUser");
|
|
return;
|
|
}
|
|
case 433: /* #define ERR_NICKNAMEINUSE 433 */
|
|
case 438:
|
|
case 453:
|
|
{
|
|
char *nickname = strtok(casevar[4], " ");
|
|
char *badnickname = ":Nickname";
|
|
|
|
if ( !strcasecmp(nickname,badnickname) ) // bug with ircd, the nickname actually shifts position.
|
|
{
|
|
nickname = strtok(casevar[3], " ");
|
|
}
|
|
|
|
// IRC_Printf(irc, DEFAULTCONSOLE, COLOURRED "ERROR: <%s> is already in use.\n",nickname);
|
|
|
|
IRC_TryNewNick(irc, nickname);
|
|
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);
|
|
return;
|
|
}
|
|
case 670: /* RPL_STARTTLS */
|
|
{
|
|
netfuncs->SetTLSClient(irc->socket, irc->server);
|
|
irc->tlsmode = TLS_START;
|
|
irc->nicktries = 0;
|
|
IRC_SetPass(irc, irc->pwd);
|
|
IRC_SetNick(irc, irc->nick);
|
|
IRC_SetUser(irc, irc_username.string);
|
|
return;
|
|
}
|
|
case 691: /* ERR_STARTTLS */
|
|
{
|
|
IRC_Printf(irc, DEFAULTCONSOLE, COLOURYELLOW "STARTTLS Failed: %s\n", casevar[3]);
|
|
netfuncs->Close(irc->socket);
|
|
irc->socket = invalid_handle;
|
|
return;
|
|
}
|
|
}
|
|
|
|
IRC_Printf(irc, DEFAULTCONSOLE, "%s\n", msg); // if no raw number exists, print the thing
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
static 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;
|
|
}
|
|
static void IRC_ICE_Update(ircclient_t *irc, struct ircice_s *ice, char updatetype)
|
|
{
|
|
//'+' propose ('hey, can I call you please?')
|
|
//'=' offer ('these are my details')
|
|
//'*' finalise ('this is what I'm going to use')
|
|
//'-' reject ('get lost, I don't want to talk to you any more')
|
|
//'%' candiate ('try this address')
|
|
//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));
|
|
}
|
|
|
|
static 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);
|
|
}
|
|
|
|
static 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);
|
|
}
|
|
|
|
static 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: %s\n", token);
|
|
}
|
|
|
|
static 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");
|
|
}
|
|
|
|
qboolean IRC_ConsoleLink(void)
|
|
{
|
|
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;
|
|
// cmdfuncs->Argv(0, text, sizeof(text));
|
|
cmdfuncs->Argv(1, link, sizeof(link));
|
|
cmdfuncs->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;
|
|
}
|
|
|
|
//==================================================
|
|
|
|
static int IRC_ClientFrame(ircclient_t *irc)
|
|
{
|
|
char prefix[256];
|
|
int ret;
|
|
char *nextmsg, *msg;
|
|
char *temp;
|
|
char token[1024];
|
|
char var[9][1000];
|
|
|
|
int i = 1;
|
|
|
|
ret = netfuncs->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 (drawfuncs)
|
|
drawfuncs->LocalSound ("misc/talk.wav", 256, 1);
|
|
|
|
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
|
|
if (Q_snprintfz(link, sizeof(link), "^["COLOURGREEN"%s\\act\\user\\who\\%s^]", prefix, prefix))
|
|
Q_snprintf(link, sizeof(link), "%s", 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 (confuncs)
|
|
{
|
|
char oldname[256];
|
|
char newname[256];
|
|
Q_snprintf(oldname, sizeof(oldname), irc->id, prefix);
|
|
Q_snprintf(newname, sizeof(newname), irc->id, col+1);
|
|
confuncs->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, "QUIT ", 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 ":%s QUIT %s\n", prefix, msg+5);*/
|
|
}
|
|
else if (!strncmp(msg, "PART ", 5))
|
|
{
|
|
char *exc = strchr(prefix, '!');
|
|
COM_Parse(msg+5, token, sizeof(token));
|
|
if (exc)
|
|
{
|
|
*exc = '\0';
|
|
IRC_Printf(irc, token, "%s leaves channel %s\n", prefix, token);
|
|
}
|
|
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 ":%s JOIN %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, "TOPIC ", 5))
|
|
{
|
|
char *topic = COM_Parse(msg+5, token, sizeof(token));
|
|
while (*topic == ' ')
|
|
topic++;
|
|
if (*topic++ == ':')
|
|
{
|
|
char *exc = strchr(prefix, '!');
|
|
if (exc)
|
|
*exc = 0;
|
|
IRC_Printf(irc, token, COLOURGREEN "%s changes topic to %s\n", prefix, topic);
|
|
}
|
|
else IRC_Printf(irc, DEFAULTCONSOLE, COLOURGREEN ":%s TOPIC %s\n", prefix, msg+5);
|
|
}
|
|
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, "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.
|
|
|
|
void IRC_Frame(double realtime, double gametime)
|
|
{
|
|
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 = netfuncs->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)
|
|
{
|
|
netfuncs->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);
|
|
}
|
|
}
|
|
|
|
void IRC_Command(ircclient_t *ircclient, char *dest, char *args)
|
|
{
|
|
char token[1024];
|
|
char *msg;
|
|
|
|
msg = COM_Parse(args, token, sizeof(token));
|
|
|
|
if (*token == '/')
|
|
{
|
|
if (!strcmp(token+1, "server"))
|
|
{ //selects the default server without connecting anywhere, for main console to use.
|
|
msg = COM_Parse(msg, token, sizeof(token));
|
|
|
|
ircclient = IRC_FindAccount(token);
|
|
if (!ircclient)
|
|
IRC_Printf(ircclient, dest, "No such connection\n");
|
|
else if (ircclients == ircclient)
|
|
IRC_Printf(ircclient, dest, "Connection is already the default.\n");
|
|
else
|
|
{
|
|
IRC_MakeDefault(ircclient);
|
|
IRC_Printf(ircclient, dest, "Connection is now default.\n");
|
|
}
|
|
}
|
|
else 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)
|
|
cvarfuncs->GetString("name", nick, sizeof(nick));
|
|
|
|
ircclient = IRC_FindAccount(server);
|
|
if (ircclient)
|
|
{
|
|
if (handleisvalid(ircclient->socket)) //don't need to do anything.
|
|
{
|
|
IRC_Printf(ircclient, dest, "IRC connection to %s already registered\n", server);
|
|
return; //silently ignore it if the account already exists
|
|
}
|
|
}
|
|
else
|
|
ircclient = IRC_Create(server, nick, irc_realname.string, irc_hostname.string, irc_username.string, password, channels);
|
|
if (ircclient)
|
|
{
|
|
IRC_MakeDefault(ircclient);
|
|
ircclient->persist |= !strcmp(token+1, "connect");
|
|
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.
|
|
cvarfuncs->SetString(irc_nick.name, token);
|
|
else
|
|
{
|
|
if (!handleisvalid(ircclient->socket))
|
|
Q_strlcpy(ircclient->primarynick, token, sizeof(ircclient->primarynick));
|
|
ircclient->nicktries = 0;
|
|
IRC_SetNick(ircclient, token);
|
|
}
|
|
|
|
IRC_WriteConfig();
|
|
}
|
|
else if (!strcmp(token+1, "user"))
|
|
{
|
|
msg = COM_Parse(msg, token, sizeof(token));
|
|
cvarfuncs->SetString(irc_username.name, token);
|
|
if (ircclient)
|
|
IRC_SetUser(ircclient, token);
|
|
|
|
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\\type\\irc /server \"%s\"^]\n", e->server, 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, dest, "***^3%s^7 %s\n", ircclient->nick, msg);
|
|
}
|
|
}
|
|
else if (!strcmp(token+1, "topic"))
|
|
{
|
|
if (!*dest)
|
|
IRC_Printf(ircclient, DEFAULTCONSOLE, "No channel joined. Try /join #<channel>\n");
|
|
else
|
|
{
|
|
if(*msg <= ' ' && *msg)
|
|
msg++;
|
|
IRC_AddClientMessage(ircclient, va("TOPIC %s :%s", dest, msg));
|
|
}
|
|
}
|
|
else
|
|
IRC_Printf(ircclient, dest, "Command not recognised\n");
|
|
}
|
|
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 = args;
|
|
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);
|
|
}
|
|
}
|