pissircd/src/api-isupport.c

328 lines
8.6 KiB
C

/************************************************************************
* UnrealIRCd - Unreal Internet Relay Chat Daemon - src/api-isupport.c
* (c) 2004 Dominick Meglio and the UnrealIRCd Team
*
* See file AUTHORS in IRC package for additional names of
* the programmers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "unrealircd.h"
ISupport *ISupports; /* List of ISUPPORT (005) tokens */
#define MAXISUPPORTLINES 10
MODVAR char *ISupportStrings[MAXISUPPORTLINES+1];
void isupport_add_sorted(ISupport *is);
void make_isupportstrings(void);
/** Easier way to set a 005 name or name=value.
* @param name Name of the 005 token
* @param value Value of the 005 token (or NULL)
* @note The 'name' 005 token will be overwritten if it already exists.
* The 'value' may be NULL, in which case if there was a value
* it will be unset.
*/
void ISupportSet(Module *module, const char *name, const char *value)
{
ISupport *is = ISupportFind(name);
if (!is)
is = ISupportAdd(module, name, value);
ISupportSetValue(is, value);
}
/** Easy way to set a 005 name=value with printf style formatting.
* @param name Name of the 005 token
* @param pattern Value pattern for the 005 token (or NULL)
* @param ... Any variables needed for 'pattern'.
* @note The 'name' 005 token will be overwritten if it already exists.
* The 'pattern' may be NULL, in which case if there was a value
* it will be unset.
*/
void ISupportSetFmt(Module *module, const char *name, const char *pattern, ...)
{
const char *value = NULL;
char buf[256];
va_list vl;
if (pattern)
{
va_start(vl, pattern);
ircvsnprintf(buf, sizeof(buf), pattern, vl);
va_end(vl);
value = buf;
}
ISupportSet(module, name, value);
}
void ISupportDelByName(const char *name)
{
ISupport *is = ISupportFind(name);
if (is)
ISupportDel(is);
}
extern void set_isupport_extban(void);
extern void set_isupport_targmax(void);
/**
* Initializes the builtin isupport tokens.
*/
void isupport_init(void)
{
ISupportSet(NULL, "INVEX", NULL);
ISupportSet(NULL, "EXCEPTS", NULL);
ISupportSet(NULL, "ELIST", "MNUCT");
ISupportSet(NULL, "CASEMAPPING", "ascii");
ISupportSet(NULL, "NETWORK", NETWORK_NAME_005);
ISupportSetFmt(NULL, "CHANMODES",
CHPAR1 "%s,%s,%s,%s",
EXPAR1, EXPAR2, EXPAR3, EXPAR4);
ISupportSet(NULL, "CHANTYPES", "#");
ISupportSetFmt(NULL, "MODES", "%d", MAXMODEPARAMS);
ISupportSetFmt(NULL, "SILENCE", "%d", SILENCE_LIMIT);
if (WATCH_AWAY_NOTIFICATION)
ISupportSet(NULL, "WATCHOPTS", "A");
else
ISupportDelByName("WATCHOPTS");
ISupportSetFmt(NULL, "WATCH", "%d", MAXWATCH);
ISupportSet(NULL, "WALLCHOPS", NULL);
ISupportSetFmt(NULL, "AWAYLEN", "%d", iConf.away_length);
ISupportSetFmt(NULL, "KICKLEN", "%d", iConf.kick_length);
ISupportSetFmt(NULL, "TOPICLEN", "%d", iConf.topic_length);
ISupportSetFmt(NULL, "QUITLEN", "%d", iConf.quit_length);
ISupportSetFmt(NULL, "CHANNELLEN", "%d", CHANNELLEN);
ISupportSetFmt(NULL, "MINNICKLEN", "%d", iConf.min_nick_length);
ISupportSetFmt(NULL, "NICKLEN", "%d", iConf.nick_length);
ISupportSetFmt(NULL, "MAXNICKLEN", "%d", NICKLEN);
ISupportSetFmt(NULL, "MAXLIST", "b:%d,e:%d,I:%d", MAXBANS, MAXBANS, MAXBANS);
ISupportSetFmt(NULL, "CHANLIMIT", "#:%d", (int)dynamic_set.settings[SET_MAX_CHANNELS_PER_USER].number);
ISupportSet(NULL, "SAFELIST", NULL);
ISupportSet(NULL, "NAMESX", NULL);
if (UHNAMES_ENABLED)
ISupportSet(NULL, "UHNAMES", NULL);
else
ISupportDelByName("UHNAMES");
ISupportSet(NULL, "DEAF", "d");
set_isupport_extban(); /* EXTBAN=xyz */
set_isupport_targmax(); /* TARGMAX=... */
}
/**
* Sets or changes the value of an existing isupport token.
*
* @param isupport The pointer to the isupport handle.
* @param value The new value of the token (NULL indicates no value).
*/
void ISupportSetValue(ISupport *isupport, const char *value)
{
safe_strdup(isupport->value, value);
make_isupportstrings();
}
/**
* Returns an isupport handle based on the given token name.
*
* @param token The isupport token to search for.
* @return Returns the handle to the isupport token if it was found,
* otherwise NULL is returned.
*/
ISupport *ISupportFind(const char *token)
{
ISupport *isupport;
for (isupport = ISupports; isupport; isupport = isupport->next)
{
if (!strcasecmp(token, isupport->token))
return isupport;
}
return NULL;
}
/**
* Adds a new isupport token.
*
* @param module The module which owns this token.
* @param token The name of the token to create.
* @param value The value of the token (NULL indicates no value).
* @return Returns the handle to the new token if successful, otherwise NULL.
* The module's error code contains specific information about the
* error.
*/
ISupport *ISupportAdd(Module *module, const char *token, const char *value)
{
ISupport *isupport;
const char *c;
if (ISupportFind(token))
{
if (module)
module->errorcode = MODERR_EXISTS;
return NULL;
}
/* draft-brocklesby-irc-isupport:
* token = a-zA-Z0-9 and 20 or less characters
* value = ASCII 0x21 - 0x7E
*/
for (c = token; c && *c; c++)
{
if (!isalnum(*c))
{
if (module)
module->errorcode = MODERR_INVALID;
return NULL;
}
}
if (!token || !*token || c-token > 20)
{
if (module)
module->errorcode = MODERR_INVALID;
return NULL;
}
for (c = value; c && *c; c++)
{
if (*c < '!' || *c > '~')
{
if (module)
module->errorcode = MODERR_INVALID;
return NULL;
}
}
isupport = safe_alloc(sizeof(ISupport));
isupport->owner = module;
safe_strdup(isupport->token, token);
if (value)
safe_strdup(isupport->value, value);
isupport_add_sorted(isupport);
make_isupportstrings();
if (module)
{
ModuleObject *isupportobj = safe_alloc(sizeof(ModuleObject));
isupportobj->object.isupport = isupport;
isupportobj->type = MOBJ_ISUPPORT;
AddListItem(isupportobj, module->objects);
module->errorcode = MODERR_NOERROR;
}
return isupport;
}
/**
* Removes the specified isupport token.
*
* @param isupport The token to remove.
*/
void ISupportDel(ISupport *isupport)
{
DelListItem(isupport, ISupports);
if (isupport->owner)
{
ModuleObject *mo;
for (mo = isupport->owner->objects; mo; mo = mo->next)
{
if (mo->type == MOBJ_ISUPPORT && mo->object.isupport == isupport)
{
DelListItem(mo, isupport->owner->objects);
safe_free(mo);
break;
}
}
}
safe_free(isupport->token);
safe_free(isupport->value);
safe_free(isupport);
make_isupportstrings();
}
/**
* Builds isupport token strings.
* Respects both the 13 token limit and the 512 buffer limit.
*/
void make_isupportstrings(void)
{
int i;
#define ISUPPORTLEN BUFSIZE-HOSTLEN-NICKLEN-39
int bufsize = ISUPPORTLEN;
int tokcnt = 0;
ISupport *isupport;
char tmp[ISUPPORTLEN];
/* Free any previous strings */
for (i = 0; ISupportStrings[i]; i++)
safe_free(ISupportStrings[i]);
i = 0;
ISupportStrings[i] = safe_alloc(bufsize+1);
for (isupport = ISupports; isupport; isupport = isupport->next)
{
if (isupport->value)
snprintf(tmp, sizeof(tmp), "%s=%s", isupport->token, isupport->value);
else
strlcpy(tmp, isupport->token, sizeof(tmp));
tokcnt++;
if ((strlen(ISupportStrings[i]) + strlen(tmp) + 1 >= ISUPPORTLEN) || (tokcnt == 13))
{
/* No room or max tokens reached: start a new buffer */
ISupportStrings[++i] = safe_alloc(bufsize+1);
tokcnt = 1;
if (i == MAXISUPPORTLINES)
abort(); /* should never happen anyway */
}
if (*ISupportStrings[i])
strlcat(ISupportStrings[i], " ", ISUPPORTLEN);
strlcat(ISupportStrings[i], tmp, ISUPPORTLEN);
}
}
void isupport_add_sorted(ISupport *n)
{
ISupport *e;
if (!ISupports)
{
ISupports = n;
return;
}
for (e = ISupports; e; e = e->next)
{
if (strcmp(n->token, e->token) < 0)
{
/* Insert us before */
if (e->prev)
e->prev->next = n;
else
ISupports = n; /* new head */
n->prev = e->prev;
n->next = e;
e->prev = n;
return;
}
if (!e->next)
{
/* Append us at end */
e->next = n;
n->prev = e;
return;
}
}
}