This repository has been archived on 2025-02-12. You can view files and clone it, but cannot push or open issues or pull requests.
NeoStats/chans.c
2003-07-30 13:58:22 +00:00

819 lines
22 KiB
C

/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2003 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id: chans.c,v 1.48 2003/07/30 13:58:22 fishwaldo Exp $
*/
#include <fnmatch.h>
#include "stats.h"
#include "dl.h"
#include "hash.h"
#include "log.h"
/** @brief initilize the channel data
*
* Initilizes the channel data and channel hash ch.
*
* @return Nothing
*/
void
init_chan_hash ()
{
ch = hash_create (C_TABLE_SIZE, 0, 0);
}
/** @brief Process the Channel TS Time
*
* Addes the channel TS time to the channel struct
*
* @param c Channel Struct of channel who's ts is being changed
* @param tstime ts time of the channel
*
* @returns Nothing
*
*/
void
Change_Chan_Ts (Chans * c, time_t tstime)
{
if (!c) {
nlog (LOG_WARNING, LOG_CORE, "Warning, Called Change_Change_Ts with null channel");
return;
}
c->tstime = tstime;
}
/** @brief Process a Topic Change
*
* Processes a Channel topic Change for particular channel and updates internals
* Also triggers off a TOPICCHANGE event for modules
*
* @param owner Char of who changed the topic. Can't be a userstruct as the user might not be online anymore
* @param c Channel Struct of channel who's topic is being changed
* @param time when the topic was changed (might have been in the past
* @param topic the new topic
*
* @return Nothing
*/
extern void
Change_Topic (char *owner, Chans * c, time_t time, char *topic)
{
char **av;
int ac = 0;
strncpy (c->topic, topic, BUFSIZE);
strncpy (c->topicowner, owner, MAXHOST);
c->topictime = time;
AddStringToList (&av, c->name, &ac);
AddStringToList (&av, owner, &ac);
AddStringToList (&av, topic, &ac);
Module_Event ("TOPICCHANGE", av, ac);
free (av);
// FreeList(av, ac);
}
/** @brief Check if a mode is set on a Channel
*
* used to check if a mode is set on a channel
*
* @param c channel to check
* @param mode is the mode to check, as a LONG
*
* @returns 1 on match, 0 on no match, -1 on error
*
*/
extern int
CheckChanMode (Chans * c, long mode)
{
ModesParm *m;
lnode_t *mn;
if (!c) {
nlog (LOG_WARNING, LOG_CORE, "Warning, CheckChanMode Called with empty channel");
return -1;
}
if (c->modes & mode) {
/* its a match */
return 1;
}
/* if we get here, we have to check the modeparm list first */
mn = list_first (c->modeparms);
while (mn) {
m = lnode_get (mn);
if (m->mode & mode) {
/* its a match */
return 1;
}
mn = list_next (c->modeparms, mn);
}
return 0;
}
/** @brief Compare channel modes from the channel hash
*
* used in ChanMode to compare modes (list_find argument)
*
* @param v actually its a ModeParm struct
* @param mode is the mode as a long
*
* @return 0 on match, 1 otherwise.
*/
int
comparemode (const void *v, const void *mode)
{
ModesParm *m = (void *) v;
if (m->mode == (long) mode) {
return 0;
} else {
return 1;
}
}
/** @brief Process a mode change on a channel
*
* process a mode change on a channel adding and deleting modes as required
*
* @param origin usually the server that sent the mode change. Not used
* @param av array of variables to pass
* @param ac number of variables n av
*
* @return 0 on error, number of modes processed on success.
*/
int
ChanMode (char *origin, char **av, int ac)
{
char *modes;
int add = 0;
int j = 2;
int i;
int modeexists;
Chans *c;
ModesParm *m;
lnode_t *mn;
c = findchan (av[0]);
if (!c) {
return 0;
}
modes = av[1];
while (*modes) {
switch (*modes) {
case '+':
add = 1;
break;
case '-':
add = 0;
break;
default:
for (i = 0; i < ((sizeof (cFlagTab) / sizeof (cFlagTab[0])) - 1); i++) {
if (*modes == cFlagTab[i].flag) {
if (add) {
if (cFlagTab[i].nickparam) {
ChangeChanUserMode (c, finduser (av[j]), 1, cFlagTab[i].mode);
j++;
} else {
if (cFlagTab[i].parameters) {
mn = list_first (c->modeparms);
modeexists = 0;
while (mn) {
m = lnode_get (mn);
/* mode limit and mode key replace current values */
if ((m->mode == MODE_LIMIT) && (cFlagTab[i].mode == MODE_LIMIT)) {
strncpy (m->param, av[j], PARAMSIZE);
j++;
modeexists = 1;
break;
} else if ((m->mode == MODE_KEY) && (cFlagTab[i].mode == MODE_KEY)) {
strncpy (m->param, av[j], PARAMSIZE);
j++;
modeexists = 1;
break;
} else if (((int *) m->mode == (int *) cFlagTab[i].mode) && !strcasecmp (m->param, av[j])) {
nlog (LOG_INFO, LOG_CORE, "Mode %c (%s) already exists, not adding again", cFlagTab[i].flag, av[j]);
j++;
modeexists = 1;
break;
}
mn = list_next (c->modeparms, mn);
}
if (modeexists != 1) {
m = smalloc (sizeof (ModesParm));
m->mode = cFlagTab[i].mode;
strncpy (m->param, av[j], PARAMSIZE);
mn = lnode_create (m);
if (list_isfull (c->modeparms)) {
nlog (LOG_CRITICAL, LOG_CORE, "Eeek, Can't add additional Modes to Channel %s. Modelist is full", c->name);
do_exit (0);
} else {
list_append (c->modeparms, mn);
}
j++;
}
} else {
c->modes |= cFlagTab[i].mode;
}
}
} else {
if (cFlagTab[i].nickparam) {
ChangeChanUserMode (c, finduser (av[j]), 0, cFlagTab[i].mode);
j++;
} else {
if (cFlagTab[i].parameters) {
mn = list_find (c->modeparms, (int *) cFlagTab[i].mode, comparemode);
if (!mn) {
nlog (LOG_INFO, LOG_CORE, "Can't find Mode %c for Chan %s", *modes, c->name);
} else {
list_delete (c->modeparms, mn);
m = lnode_get (mn);
lnode_destroy (mn);
free (m);
if (!(cFlagTab[i].mode == MODE_LIMIT || cFlagTab[i].mode == MODE_KEY))
j++;
}
} else {
c->modes &= ~cFlagTab[i].mode;
}
}
}
}
}
}
modes++;
}
return j;
}
/** @brief Process a mode change that affects a user on a channel
*
* process a mode change on a channel that affects a user
*
* @param c Channel Struct of channel mode being changed
* @param u User struct of user that mode is affecting
* @param add 1 means add, 0 means remove mode
* @param mode is the long int of the mode
*
* @return Nothing
*/
void
ChangeChanUserMode (Chans * c, User * u, int add, long mode)
{
lnode_t *cmn;
Chanmem *cm;
if (!u) {
nlog (LOG_WARNING, LOG_CORE, "Can't find user for ChangeChanUserMode");
#ifdef DEBUG
return;
#endif
}
cmn = list_find (c->chanmembers, u->nick, comparef);
if (!cmn) {
if (me.coder_debug) {
chanalert (s_Services, "ChangeChanUserMode() %s doesn't seem to be in the Chan %s", u->nick, c->name);
chandump (c->name);
UserDump (u->nick);
}
return;
}
cm = lnode_get (cmn);
if (add) {
nlog (LOG_DEBUG2, LOG_CORE, "Adding mode %ld to Channel %s User %s", mode, c->name, u->nick);
cm->flags |= mode;
} else {
nlog (LOG_DEBUG2, LOG_CORE, "Deleting Mode %ld to Channel %s User %s", mode, c->name, u->nick);
cm->flags &= ~mode;
}
}
/** @brief Create a new channel record
*
* And insert it into the hash, mallocing required memory for it and so on
* also check that the channel hash is not full
*
* @param chan name of the channel to create
*
* @returns c the newly created channel record
* @todo Dynamically resizable channel hashes
*/
Chans *
new_chan (char *chan)
{
Chans *c;
hnode_t *cn;
strcpy (segv_location, "new_chan");
c = smalloc (sizeof (Chans));
strncpy (c->name, chan, CHANLEN);
cn = hnode_create (c);
if (hash_isfull (ch)) {
nlog (LOG_CRITICAL, LOG_CORE, "Eeek, Channel Hash is full");
} else {
hash_insert (ch, cn, c->name);
}
return c;
}
/** @brief Deletes a channel record
*
* frees any memory associated with the record and removes it from the channel hash
*
* @param c the corrosponding channel structure you wish to delete
*
* @returns Nothing
*/
void
del_chan (Chans * c)
{
hnode_t *cn;
lnode_t *cm;
strcpy (segv_location, "del_chan");
cn = hash_lookup (ch, c->name);
if (!cn) {
nlog (LOG_WARNING, LOG_CORE, "Hu, Deleting a Non Existand Channel?");
return;
} else {
nlog (LOG_DEBUG2, LOG_CORE, "Deleting Channel %s", c->name);
cm = list_first (c->modeparms);
while (cm) {
free (lnode_get (cm));
cm = list_next (c->modeparms, cm);
}
list_destroy_nodes (c->modeparms);
list_destroy (c->modeparms);
list_destroy (c->chanmembers);
hash_delete (ch, cn);
hnode_destroy (cn);
free (c);
}
}
/** @brief Process a kick from a channel.
*
* In fact, this does nothing special apart from call part_chan, and processing a channel kick
* @param User u, user structure of user getting kicked
* @param channel name user being kicked from
* @param User k, the user doing the kick
*
*
*/
void
kick_chan (User * u, char *chan, User * k)
{
char **av;
int ac = 0;
Chans *c;
Chanmem *cm;
lnode_t *un;
strcpy (segv_location, "part_chan");
nlog (LOG_DEBUG2, LOG_CORE, "%s Kicking %s from %s", k->nick, u->nick, chan);
if (!u) {
nlog (LOG_WARNING, LOG_CORE, "Ehh, KIcking a Unknown User %s from Chan %s: %s", u->nick, chan, recbuf);
if (me.coder_debug) {
chanalert (s_Services, "Ehh, Kicking a Unknown User %s from Chan %s: %s", u->nick, chan, recbuf);
chandump (chan);
UserDump (u->nick);
}
return;
}
c = findchan (chan);
if (!c) {
nlog (LOG_WARNING, LOG_CORE, "Hu, Kicking a Non existant Channel? %s", chan);
return;
} else {
un = list_find (c->chanmembers, u->nick, comparef);
if (!un) {
nlog (LOG_WARNING, LOG_CORE, "Kick: hu, User %s isn't a member of this channel %s", u->nick, chan);
if (me.coder_debug) {
chanalert (s_Services, "Kick: hu, User %s isn't a member of this channel %s", u->nick, chan);
chandump (c->name);
UserDump (u->nick);
}
} else {
cm = lnode_get (un);
lnode_destroy (list_delete (c->chanmembers, un));
free (cm);
AddStringToList (&av, c->name, &ac);
AddStringToList (&av, u->nick, &ac);
Module_Event ("KICK", av, ac);
free (av);
ac = 0;
c->cur_users--;
}
nlog (LOG_DEBUG3, LOG_CORE, "Cur Users %s %d (list %d)", c->name, c->cur_users, list_count (c->chanmembers));
if (c->cur_users <= 0) {
AddStringToList (&av, c->name, &ac);
Module_Event ("DELCHAN", av, ac);
free (av);
ac = 0;
del_chan (c);
}
un = list_find (u->chans, c->name, comparef);
if (!un) {
nlog (LOG_WARNING, LOG_CORE, "Kick:Hu, User %s claims not to be part of Chan %s", u->nick, chan);
if (me.coder_debug) {
chanalert (s_Services, "Kick: Hu, User %s claims not to be part of Chan %s", u->nick, chan);
chandump (c->name);
UserDump (u->nick);
}
return;
}
if (findbot (u->nick)) {
/* its one of our bots, so add it to the botchan list */
del_bot_from_chan (u->nick, c->name);
AddStringToList (&av, c->name, &ac);
AddStringToList (&av, u->nick, &ac);
Module_Event ("KICKBOT", av, ac);
free (av);
ac = 0;
}
lnode_destroy (list_delete (u->chans, un));
}
}
/** @brief Parts a user from a channel
*
* Parts a user from a channel and raises events if required
* Events raised are PARTCHAN and DELCHAN
* if its one of our bots, also update bot channel lists
*
* @param u the User structure corrosponding to the user that left the channel
* @param chan the channel to part them from
*
* @returns Nothing
*/
void
part_chan (User * u, char *chan)
{
Chans *c;
lnode_t *un;
char **av;
Chanmem *cm;
int ac = 0;
strcpy (segv_location, "part_chan");
nlog (LOG_DEBUG2, LOG_CORE, "Parting %s from %s", u->nick, chan);
if (!u) {
nlog (LOG_WARNING, LOG_CORE, "Ehh, Parting a Unknown User %s from Chan %s: %s", u->nick, chan, recbuf);
if (me.coder_debug) {
chanalert (s_Services, "Ehh, Parting a Unknown User %s from Chan %s: %s", u->nick, chan, recbuf);
chandump (chan);
UserDump (u->nick);
}
return;
}
c = findchan (chan);
if (!c) {
nlog (LOG_WARNING, LOG_CORE, "Hu, Parting a Non existant Channel? %s", chan);
return;
} else {
un = list_find (c->chanmembers, u->nick, comparef);
if (!un) {
nlog (LOG_WARNING, LOG_CORE, "hu, User %s isn't a member of this channel %s", u->nick, chan);
if (me.coder_debug) {
chanalert (s_Services, "hu, User %s isn't a member of this channel %s", u->nick, chan);
chandump (c->name);
UserDump (u->nick);
}
} else {
cm = lnode_get (un);
lnode_destroy (list_delete (c->chanmembers, un));
free (cm);
AddStringToList (&av, c->name, &ac);
AddStringToList (&av, u->nick, &ac);
Module_Event ("PARTCHAN", av, ac);
free (av);
ac = 0;
// FreeList(av, ac);
c->cur_users--;
}
nlog (LOG_DEBUG3, LOG_CORE, "Cur Users %s %d (list %d)", c->name, c->cur_users, list_count (c->chanmembers));
if (c->cur_users <= 0) {
AddStringToList (&av, c->name, &ac);
Module_Event ("DELCHAN", av, ac);
free (av);
ac = 0;
// FreeList(av, ac);
del_chan (c);
}
un = list_find (u->chans, c->name, comparef);
if (!un) {
nlog (LOG_WARNING, LOG_CORE, "Hu, User %s claims not to be part of Chan %s", u->nick, chan);
if (me.coder_debug) {
chanalert (s_Services, "Hu, User %s claims not to be part of Chan %s", u->nick, chan);
chandump (c->name);
UserDump (u->nick);
}
return;
}
if (findbot (u->nick)) {
/* its one of our bots, so add it to the botchan list */
del_bot_from_chan (u->nick, c->name);
AddStringToList (&av, c->name, &ac);
AddStringToList (&av, u->nick, &ac);
Module_Event ("KICKBOT", av, ac);
free (av);
ac = 0;
}
lnode_destroy (list_delete (u->chans, un));
}
}
/** @brief Change channel records when a nick change occurs
*
* goes through the channel members list, changing users nicks after a nickname change occurs
*
* @param c the channel to check (as called by the user functions)
* @param newnick the New Nickname of the client
* @param oldnick the old nickname of the client
*
* @returns Nothing
* @todo What happens if one of our bots change their nick?
*/
void
change_user_nick (Chans * c, char *newnick, char *oldnick)
{
lnode_t *cm;
Chanmem *cml;
strcpy (segv_location, "change_user_nick");
cm = list_find (c->chanmembers, oldnick, comparef);
if (!cm) {
nlog (LOG_WARNING, LOG_CORE, "change_user_nick() %s isn't a member of %s", oldnick, c->name);
if (me.coder_debug) {
chanalert (s_Services, "change_user_nick() %s isn't a member of %s", oldnick, c->name);
chandump (c->name);
UserDump (oldnick);
}
return;
} else {
nlog (LOG_DEBUG3, LOG_CORE, "Change_User_Nick(): NewNick %s, OldNick %s", newnick, oldnick);
cml = lnode_get (cm);
strncpy (cml->nick, newnick, MAXNICK);
}
}
/** @brief Process a user joining a channel
*
* joins a user to a channel and raises JOINCHAN event and if required NEWCHAN events
* if the channel is new, a new channel record is requested and defaults are set
* if its one of our bots, also update the botchanlist
*
* @param u The User structure of the user joining the channel
* @param chan the channel name
*
* @returns Nothing
*/
void
join_chan (User * u, char *chan)
{
Chans *c;
lnode_t *un, *cn;
Chanmem *cm;
char **av;
int ac = 0;
strcpy (segv_location, "join_chan");
if (!u) {
nlog (LOG_WARNING, LOG_CORE, "ehhh, Joining a Unknown user to %s: %s", chan, recbuf);
return;
}
if (!strcasecmp ("0", chan)) {
/* join 0 is actually part all chans */
nlog (LOG_DEBUG2, LOG_CORE, "join_chan() -> Parting all chans %s", u->nick);
list_process (u->chans, u, part_u_chan);
return;
}
c = findchan (chan);
if (!c) {
/* its a new Channel */
nlog (LOG_DEBUG2, LOG_CORE, "join_chan() -> New Channel %s", chan);
c = new_chan (chan);
c->chanmembers = list_create (CHAN_MEM_SIZE);
c->modeparms = list_create (MAXMODES);
c->cur_users = 0;
c->topictime = 0;
c->modes = 0;
c->tstime = 0;
AddStringToList (&av, c->name, &ac);
Module_Event ("NEWCHAN", av, ac);
free (av);
ac = 0;
// FreeList(av, ac);
}
/* add this users details to the channel members hash */
cm = smalloc (sizeof (Chanmem));
strncpy (cm->nick, u->nick, MAXNICK);
cm->joint = time (NULL);
cm->flags = 0;
cn = lnode_create (cm);
nlog (LOG_DEBUG2, LOG_CORE, "adding usernode %s to Channel %s", u->nick, chan);
if (list_find (c->chanmembers, u->nick, comparef)) {
nlog (LOG_WARNING, LOG_CORE, "Adding %s to Chan %s when he is already a member?", u->nick, chan);
if (me.coder_debug) {
chanalert (s_Services, "Adding %s to Chan %s when he is already a member?", u->nick, chan);
chandump (c->name);
UserDump (u->nick);
}
return;
}
if (list_isfull (c->chanmembers)) {
nlog (LOG_CRITICAL, LOG_CORE, "ekk, Channel %s Members list is full", c->name);
lnode_destroy (cn);
free (cm);
return;
} else {
list_append (c->chanmembers, cn);
}
c->cur_users++;
un = lnode_create (c->name);
if (list_isfull (u->chans)) {
nlog (LOG_CRITICAL, LOG_CORE, "eek, User %s members list is full", u->nick);
lnode_destroy (un);
} else {
list_append (u->chans, un);
}
AddStringToList (&av, c->name, &ac);
AddStringToList (&av, u->nick, &ac);
Module_Event ("JOINCHAN", av, ac);
free (av);
// FreeList(av, ac);
nlog (LOG_DEBUG3, LOG_CORE, "Cur Users %s %d (list %d)", c->name, c->cur_users, list_count (c->chanmembers));
if (findbot (u->nick)) {
add_bot_to_chan (u->nick, c->name);
}
}
/** @brief Dump Channel information
*
* dump either the entire channel list, or a single channel detail. Used for debugging
* sends the output to the services channel
*
* @param chan the channel name to dump, or NULL for all channels
*
* @returns Nothing
*/
void
chandump (char *chan)
{
hnode_t *cn;
lnode_t *cmn;
hscan_t sc;
Chans *c;
Chanmem *cm;
char mode[10];
int i;
int j = 0;
ModesParm *m;
strcpy (segv_location, "chandump");
if (!chan) {
sendcoders ("Channels %d", hash_count (ch));
hash_scan_begin (&sc, ch);
while ((cn = hash_scan_next (&sc)) != NULL) {
c = hnode_get (cn);
sendcoders ("====================");
bzero (mode, 10);
mode[0] = '+';
for (i = 0; i < ((sizeof (cFlagTab) / sizeof (cFlagTab[0])) - 1); i++) {
if (c->modes & cFlagTab[i].mode) {
mode[++j] = cFlagTab[i].flag;
}
}
sendcoders ("Channel: %s Members: %d (List %d) Flags %s tstime %d", c->name, c->cur_users, list_count (c->chanmembers), mode, c->tstime);
sendcoders (" Topic Owner %s, TopicTime: %d, Topic %s", c->topicowner, c->topictime, c->topic);
sendcoders ("PubChan?: %d", is_pub_chan (c));
cmn = list_first (c->modeparms);
while (cmn) {
m = lnode_get (cmn);
for (i = 0; i < ((sizeof (cFlagTab) / sizeof (cFlagTab[0])) - 1); i++) {
if (m->mode & cFlagTab[i].mode) {
sendcoders (" Modes: %c Parms %s", cFlagTab[i].flag, m->param);
}
}
cmn = list_next (c->modeparms, cmn);
}
cmn = list_first (c->chanmembers);
while (cmn) {
cm = lnode_get (cmn);
bzero (mode, 10);
j = 0;
mode[0] = '+';
for (i = 0; i < ((sizeof (cFlagTab) / sizeof (cFlagTab[0])) - 1); i++) {
if (cm->flags & cFlagTab[i].mode) {
mode[++j] = cFlagTab[i].flag;
}
}
sendcoders ("Members: %s Modes %s Joined %d", cm->nick, mode, cm->joint);
cmn = list_next (c->chanmembers, cmn);
}
}
} else {
c = findchan (chan);
if (!c) {
sendcoders ("Can't find Channel %s", chan);
} else {
bzero (mode, 10);
j = 0;
mode[0] = '+';
for (i = 0; i < ((sizeof (cFlagTab) / sizeof (cFlagTab[0])) - 1); i++) {
if (c->modes & cFlagTab[i].mode) {
mode[++j] = cFlagTab[i].flag;
}
}
sendcoders ("Channel: %s Members: %d (List %d) Flags %s tstime %d", c->name, c->cur_users, list_count (c->chanmembers), mode, c->tstime);
sendcoders (" Topic Owner %s, TopicTime: %d Topic %s", c->topicowner, c->topictime, c->topic);
sendcoders ("PubChan?: %d", is_pub_chan (c));
cmn = list_first (c->modeparms);
while (cmn) {
m = lnode_get (cmn);
for (i = 0; i < ((sizeof (cFlagTab) / sizeof (cFlagTab[0])) - 1); i++) {
if (m->mode & cFlagTab[i].mode) {
sendcoders (" Modes: %c Parms %s", cFlagTab[i].flag, m->param);
}
}
cmn = list_next (c->modeparms, cmn);
}
cmn = list_first (c->chanmembers);
while (cmn) {
cm = lnode_get (cmn);
bzero (mode, 10);
mode[0] = '+';
j = 0;
for (i = 0; i < ((sizeof (cFlagTab) / sizeof (cFlagTab[0])) - 1); i++) {
if (cm->flags & cFlagTab[i].mode) {
mode[++j] = cFlagTab[i].flag;
}
}
sendcoders ("Members: %s Modes %s Joined: %d", cm->nick, mode, cm->joint);
cmn = list_next (c->chanmembers, cmn);
}
}
}
}
/** @brief Find a channel
*
* Finds the channel structure for the channel named chan, or NULL if it can't be found
*
* @param chan the channel name to find
*
* @returns The Channel structure for chan, or NULL if it can't be found.
*/
Chans *
findchan (char *chan)
{
Chans *c;
hnode_t *cn;
strcpy (segv_location, "findchan");
cn = hash_lookup (ch, chan);
if (cn) {
c = hnode_get (cn);
return c;
} else {
nlog (LOG_DEBUG3, LOG_CORE, "FindChan(%s) -> Not Found", chan);
return NULL;
}
}