/* NeoStats - IRC Statistical Services ** Copyright (c) 1999-2004 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$ */ #include "stats.h" #include "ircd.h" #include "dl.h" #include "hash.h" #include "log.h" #include "users.h" #include "chans.h" #include "exclude.h" #ifdef SQLSRV #include "sqlsrv/rta.h" #endif /** @brief Chanmem structure * */ typedef struct Chanmem { char nick[MAXNICK]; time_t jointime; long flags; void *moddata[NUM_MODULES]; } Chanmem; hash_t *ch; /** @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 SetChanTS (Chans * c, const time_t tstime) { 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 */ void ChanTopic (const char* chan, const char *owner, const char* ts, const char *topic) { char **av; int ac = 0; Chans *c; time_t time; c = findchan (chan); if (!c) { nlog (LOG_WARNING, LOG_CORE, "ChanTopic: can't find channel %s", chan); return; } if(ts) { time = atoi (ts); } else { time = me.now; } if(topic) { strlcpy (c->topic, topic, BUFSIZE); } else { c->topic[0] = 0; } strlcpy (c->topicowner, owner, MAXHOST); c->topictime = time; AddStringToList (&av, c->name, &ac); AddStringToList (&av, (char*)owner, &ac); if(topic) { AddStringToList (&av, (char*)topic, &ac); } else { AddStringToList (&av, "", &ac); } ModuleEvent (EVENT_TOPICCHANGE, av, ac); free (av); } /** @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 * */ int CheckChanMode (Chans * c, long mode) { ModesParm *m; lnode_t *mn; if (!c) { nlog (LOG_WARNING, LOG_CORE, "CheckChanMode: tied to check modes of 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 ChanModes 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. */ static 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; char **data; int datasize = 0; c = findchan (av[0]); if (!c) { return 0; } AddStringToList(&data, origin, &datasize); for (i = 0; i < ac; i++) { AddStringToList(&data, av[i], &datasize); } ModuleEvent(EVENT_CHANMODE, data, datasize); free(data); modes = av[1]; while (*modes) { switch (*modes) { case '+': add = 1; break; case '-': add = 0; break; default: for (i = 0; i < ircd_cmodecount; i++) { if (*modes == chan_modes[i].flag) { if (add) { if (chan_modes[i].nickparam) { ChanUserMode (av[0], av[j], 1, chan_modes[i].mode); j++; } else { if (chan_modes[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 == CMODE_LIMIT) && (chan_modes[i].mode == CMODE_LIMIT)) { strlcpy (m->param, av[j], PARAMSIZE); j++; modeexists = 1; break; } else if ((m->mode == CMODE_KEY) && (chan_modes[i].mode == CMODE_KEY)) { strlcpy (m->param, av[j], PARAMSIZE); j++; modeexists = 1; break; } else if (((int *) m->mode == (int *) chan_modes[i].mode) && !ircstrcasecmp (m->param, av[j])) { nlog (LOG_INFO, LOG_CORE, "ChanMode: Mode %c (%s) already exists, not adding again", chan_modes[i].flag, av[j]); j++; modeexists = 1; break; } mn = list_next (c->modeparms, mn); } if (modeexists != 1) { m = smalloc (sizeof (ModesParm)); m->mode = chan_modes[i].mode; strlcpy (m->param, av[j], PARAMSIZE); mn = lnode_create (m); if (list_isfull (c->modeparms)) { nlog (LOG_CRITICAL, LOG_CORE, "ChanMode: modelist is full adding to channel %s", c->name); do_exit (NS_EXIT_ERROR, "List full - see log file"); } else { list_append (c->modeparms, mn); } j++; } } else { c->modes |= chan_modes[i].mode; } } } else { if (chan_modes[i].nickparam) { ChanUserMode (av[0], av[j], 0, chan_modes[i].mode); j++; } else { if (chan_modes[i].parameters) { mn = list_find (c->modeparms, (int *) chan_modes[i].mode, comparemode); if (!mn) { nlog (LOG_INFO, LOG_CORE, "ChanMode: can't find mode %c for channel %s", *modes, c->name); } else { list_delete (c->modeparms, mn); m = lnode_get (mn); lnode_destroy (mn); free (m); if (!(chan_modes[i].mode == CMODE_LIMIT)) j++; } } else { c->modes &= ~chan_modes[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 ChanUserMode (const char* chan, const char* nick, int add, long mode) { lnode_t *cmn; Chanmem *cm; Chans * c; User * u; u = finduser(nick); if (!u) { nlog (LOG_WARNING, LOG_CORE, "ChanUserMode: can't find user %s", nick); return; } c = findchan(chan); if (!c) { nlog (LOG_WARNING, LOG_CORE, "ChanUserMode: can't find channel %s", chan); return; } cmn = list_find (c->chanmembers, u->nick, comparef); if (!cmn) { if (me.debug_mode) { chanalert (s_Services, "ChanUserMode: %s is not a member of channel %s", u->nick, c->name); ChanDump (c->name); UserDump (u->nick); } return; } cm = lnode_get (cmn); if (add) { nlog (LOG_DEBUG2, LOG_CORE, "ChanUserMode: Adding mode %ld to Channel %s User %s", mode, c->name, u->nick); cm->flags |= mode; } else { nlog (LOG_DEBUG2, LOG_CORE, "ChanUserMode: 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 */ static Chans * new_chan (const char *chan) { char **av; int ac = 0; Chans *c; hnode_t *cn; SET_SEGV_LOCATION(); c = calloc (sizeof (Chans), 1); strlcpy (c->name, chan, CHANLEN); cn = hnode_create (c); if (hash_isfull (ch)) { nlog (LOG_CRITICAL, LOG_CORE, "new_chan: channel hash is full"); } else { hash_insert (ch, cn, c->name); } c->chanmembers = list_create (CHAN_MEM_SIZE); c->modeparms = list_create (MAXMODES); c->cur_users = 0; c->topictime = 0; c->modes = 0; c->tstime = me.now; c->flags = 0; /* check exclusions */ ns_do_exclude_chan(c); AddStringToList (&av, c->name, &ac); ModuleEvent (EVENT_NEWCHAN, av, ac); free (av); 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; SET_SEGV_LOCATION(); cn = hash_lookup (ch, c->name); if (!cn) { nlog (LOG_WARNING, LOG_CORE, "del_chan: channel %s not found.", c->name); return; } else { nlog (LOG_DEBUG2, LOG_CORE, "del_chan: 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 kickby, the user nick or servername doing the kick * @param chan, channel name user being kicked from * @param kicked, the user nick getting kicked * @param kickreason the reason the user was kicked * */ void kick_chan (const char *kickby, const char *chan, const char *kicked, const char *kickreason) { char **av; int ac = 0; Chans *c; Chanmem *cm; lnode_t *un; User *u; SET_SEGV_LOCATION(); u = finduser (kicked); if (!u) { nlog (LOG_WARNING, LOG_CORE, "kick_chan: user %s not found %s %s", kicked, chan, kickby); if (me.debug_mode) { chanalert (s_Services, "kick_chan: user %s not found %s %s", kicked, chan, kickby); ChanDump (chan); } return; } nlog (LOG_DEBUG2, LOG_CORE, "kick_chan: %s kicking %s from %s for %s", kickby, u->nick, chan, kickreason ? kickreason : "no reason"); c = findchan (chan); if (!c) { nlog (LOG_WARNING, LOG_CORE, "kick_chan: channel %s not found", chan); return; } else { un = list_find (c->chanmembers, u->nick, comparef); if (!un) { nlog (LOG_WARNING, LOG_CORE, "kick_chan: %s isn't a member of channel %s", u->nick, chan); if (me.debug_mode) { chanalert (s_Services, "kick_chan: %s isn't a member of 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); AddStringToList (&av, (char *)kickby, &ac); if (kickreason != NULL) { AddStringToList (&av, (char*)kickreason, &ac); } ModuleEvent (EVENT_KICK, av, ac); free (av); ac = 0; c->cur_users--; } 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); AddStringToList (&av, (char *)kickby, &ac); if (kickreason != NULL) { AddStringToList (&av, (char*)kickreason, &ac); } ModuleEvent (EVENT_KICKBOT, av, ac); free (av); ac = 0; } un = list_find (u->chans, c->name, comparef); if (!un) { nlog (LOG_WARNING, LOG_CORE, "kick_chan: %s not found in channel %s", u->nick, chan); if (me.debug_mode) { chanalert (s_Services, "kick_chan: %s not found in channel %s", u->nick, chan); ChanDump (c->name); UserDump (u->nick); } } else { lnode_destroy (list_delete (u->chans, un)); } nlog (LOG_DEBUG3, LOG_CORE, "kick_chan: cur users %s %ld (list %d)", c->name, c->cur_users, (int)list_count (c->chanmembers)); if (c->cur_users <= 0) { AddStringToList (&av, c->name, &ac); ModuleEvent (EVENT_DELCHAN, av, ac); free (av); ac = 0; del_chan (c); } } } /** @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 * @param reason the reason the user parted, if any * * @returns Nothing */ void part_chan (User * u, const char *chan, const char *reason) { Chans *c; lnode_t *un; char **av; Chanmem *cm; int ac = 0; SET_SEGV_LOCATION(); if (!u) { nlog (LOG_WARNING, LOG_CORE, "part_chan: trying to part NULL user from %s", chan); if (me.debug_mode) { chanalert (s_Services, "part_chan: trying to part NULL user from %s", chan); ChanDump (chan); } return; } nlog (LOG_DEBUG2, LOG_CORE, "part_chan: parting %s from %s", u->nick, chan); c = findchan (chan); if (!c) { nlog (LOG_WARNING, LOG_CORE, "part_chan: channel %s not found", chan); return; } else { un = list_find (c->chanmembers, u->nick, comparef); if (!un) { nlog (LOG_WARNING, LOG_CORE, "part_chan: user %s isn't a member of channel %s", u->nick, chan); if (me.debug_mode) { chanalert (s_Services, "part_chan: user %s isn't a member of 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); if (reason != NULL) { AddStringToList (&av, (char*)reason, &ac); } ModuleEvent (EVENT_PARTCHAN, av, ac); free (av); ac = 0; c->cur_users--; } 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); if (reason != NULL) { AddStringToList (&av, (char*)reason, &ac); } ModuleEvent (EVENT_PARTBOT, av, ac); free (av); ac = 0; } un = list_find (u->chans, c->name, comparef); if (!un) { nlog (LOG_WARNING, LOG_CORE, "part_chan: user %s not found in channel %s", u->nick, chan); if (me.debug_mode) { chanalert (s_Services, "part_chan: user %s not found in channel %s", u->nick, chan); ChanDump (c->name); UserDump (u->nick); } return; } else { lnode_destroy (list_delete (u->chans, un)); } nlog (LOG_DEBUG3, LOG_CORE, "part_chan: cur users %s %ld (list %d)", c->name, c->cur_users, (int)list_count (c->chanmembers)); if (c->cur_users <= 0) { AddStringToList (&av, c->name, &ac); ModuleEvent (EVENT_DELCHAN, av, ac); free (av); ac = 0; del_chan (c); } } } /** @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 ChanNickChange (Chans * c, const char *newnick, const char *oldnick) { lnode_t *cm; Chanmem *cml; SET_SEGV_LOCATION(); cm = list_find (c->chanmembers, oldnick, comparef); if (!cm) { nlog (LOG_WARNING, LOG_CORE, "ChanNickChange: %s isn't a member of %s", oldnick, c->name); if (me.debug_mode) { chanalert (s_Services, "ChanNickChange: %s isn't a member of %s", oldnick, c->name); ChanDump (c->name); UserDump (oldnick); } return; } else { nlog (LOG_DEBUG3, LOG_CORE, "ChanNickChange: newnick %s, oldnick %s", newnick, oldnick); cml = lnode_get (cm); strlcpy (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 (const char* nick, const char *chan) { User* u; Chans *c; lnode_t *un, *cn; Chanmem *cm; char **av; int ac = 0; SET_SEGV_LOCATION(); u = finduser (nick); if (!u) { nlog (LOG_WARNING, LOG_CORE, "join_chan: tried to join unknown user %s to channel %s", nick, chan); return; } if (!ircstrcasecmp ("0", chan)) { /* join 0 is actually part all chans */ nlog (LOG_DEBUG2, LOG_CORE, "join_chan: parting %s from all channels", u->nick); list_process (u->chans, u, UserPart); 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); } /* add this users details to the channel members hash */ cm = smalloc (sizeof (Chanmem)); strlcpy (cm->nick, u->nick, MAXNICK); cm->jointime = me.now; cm->flags = 0; cn = lnode_create (cm); nlog (LOG_DEBUG2, LOG_CORE, "join_chan: adding usernode %s to channel %s", u->nick, chan); if (list_find (c->chanmembers, u->nick, comparef)) { nlog (LOG_WARNING, LOG_CORE, "join_chan: tried to add %s to channel %s but they are already a member", u->nick, chan); if (me.debug_mode) { chanalert (s_Services, "join_chan: tried to add %s to channel %s but they are already a member", u->nick, chan); ChanDump (c->name); UserDump (u->nick); } return; } if (list_isfull (c->chanmembers)) { nlog (LOG_CRITICAL, LOG_CORE, "join_chan: channel %s member 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, "join_chan: user %s member list is full", u->nick); lnode_destroy (un); } else { list_append (u->chans, un); } AddStringToList (&av, c->name, &ac); AddStringToList (&av, u->nick, &ac); ModuleEvent (EVENT_JOINCHAN, av, ac); free (av); nlog (LOG_DEBUG3, LOG_CORE, "join_chan: cur users %s %ld (list %d)", c->name, c->cur_users, (int)list_count (c->chanmembers)); if (findbot (u->nick)) { nlog(LOG_DEBUG3, LOG_CORE, "join_chan: joining bot %s to channel %s", u->nick, c->name); 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 */ static void dumpchan (Chans* c) { lnode_t *cmn; Chanmem *cm; char mode[10]; int i; int j = 0; ModesParm *m; bzero (mode, 10); mode[0] = '+'; for (i = 0; i < ircd_cmodecount; i++) { if (c->modes & chan_modes[i].mode) { mode[++j] = chan_modes[i].flag; } } debugtochannel("Channel: %s", c->name); debugtochannel("Mode: %s tstime %ld", mode, (long)c->tstime); debugtochannel("TopicOwner: %s TopicTime: %ld Topic: %s", c->topicowner, (long)c->topictime, c->topic); debugtochannel("PubChan?: %d", is_pub_chan (c)); debugtochannel("Flags: %lx", c->flags); cmn = list_first (c->modeparms); while (cmn) { m = lnode_get (cmn); for (i = 0; i < ircd_cmodecount; i++) { if (m->mode & chan_modes[i].mode) { debugtochannel("Modes: %c Parms %s", chan_modes[i].flag, m->param); } } cmn = list_next (c->modeparms, cmn); } debugtochannel("Members: %ld (List %d)", c->cur_users, (int)list_count (c->chanmembers)); cmn = list_first (c->chanmembers); while (cmn) { cm = lnode_get (cmn); bzero (mode, 10); j = 0; mode[0] = '+'; for (i = 0; i < ircd_cmodecount; i++) { if (cm->flags & chan_modes[i].mode) { mode[++j] = chan_modes[i].flag; } } debugtochannel(" %s Modes %s Joined: %ld", cm->nick, mode, (long)cm->jointime); cmn = list_next (c->chanmembers, cmn); } debugtochannel("========================================"); } void ChanDump (const char *chan) { hnode_t *cn; hscan_t sc; Chans *c; SET_SEGV_LOCATION(); debugtochannel("================CHANDUMP================"); if (!chan) { debugtochannel("Channels %d", (int)hash_count (ch)); hash_scan_begin (&sc, ch); while ((cn = hash_scan_next (&sc)) != NULL) { c = hnode_get (cn); dumpchan(c); } } else { c = findchan (chan); if (c) { dumpchan(c); } else { debugtochannel("ChanDump: can't find channel %s", chan); } } } /** @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 (const char *chan) { Chans *c; hnode_t *cn; cn = hash_lookup (ch, chan); if (cn) { c = hnode_get (cn); return c; } nlog (LOG_DEBUG3, LOG_CORE, "findchan: %s not found", chan); return NULL; } /** @brief Returns if the nick is a member of the channel * * Returns 1 if nick is part of the channel, 0 if they are not * * @param chan the channel to check * @param u the user to check * * @returns 1 if they are, 0 if they are not */ int IsChanMember (Chans *c, User *u) { if (!u || !c) { return 0; } if (list_find (c->chanmembers, u->nick, comparef)) { return 1; } return 0; } /** @brief Returns if the nick has a particular channel status e.g. ChanOp * * @param chan the channel to check * @param nick the nick to check * * @returns 1 if they are, 0 if they are not */ int test_chan_user_mode(char* chan, char* nick, int flag) { User* u; Chans* c; lnode_t *cmn; Chanmem *cm; u = finduser(nick); c = findchan(chan); if (!u || !c) { return 0; } cmn = list_find (c->chanmembers, nick, comparef); if (!cmn) { return 0; } cm = lnode_get (cmn); if (cm->flags & flag) { return 1; } return 0; } #ifdef SQLSRV /* display the channel modes */ /* BUFSIZE is probably too small.. oh well */ static char chanmodes[BUFSIZE]; void *display_chanmodes (void *tbl, char *col, char *sql, void *row) { Chans *c = row; lnode_t *cmn; char tmp[BUFSIZE]; int i; int j = 1; ModesParm *m; chanmodes[0] = '+'; for (i = 0; i < ircd_cmodecount; i++) { if (c->modes & chan_modes[i].mode) { chanmodes[j++] = chan_modes[i].flag; } } chanmodes[j++] = '\0'; cmn = list_first (c->modeparms); while (cmn) { m = lnode_get (cmn); for (i = 0; i < ircd_cmodecount; i++) { if (m->mode & chan_modes[i].mode) { ircsnprintf(tmp, BUFSIZE, " +%c %s", chan_modes[i].flag, m->param); strlcat(chanmodes, tmp, BUFSIZE); } } cmn = list_next (c->modeparms, cmn); } return chanmodes; } /* its huge, because we can have a *LOT* of users in a channel */ static char chanusers[BUFSIZE*10]; void *display_chanusers (void *tbl, char *col, char *sql, void *row) { Chans *c = row; lnode_t *cmn; char sjoin[BUFSIZE]; char mode[BUFSIZE]; char final[BUFSIZE*2]; int i; int j = 0; int k = 0; Chanmem *cm; chanusers[0] = '\0'; cmn = list_first (c->chanmembers); while (cmn) { cm = lnode_get (cmn); j = 0; k = 1; mode[0] = '+'; for (i = 0; i < ircd_cmodecount; i++) { if (cm->flags & chan_modes[i].mode) { if (chan_modes[i].sjoin != 0) sjoin[j++] = chan_modes[i].sjoin; else mode[k++] = chan_modes[i].flag; } } mode[k++] = '\0'; sjoin[j++] = '\0'; if (k > 2) ircsnprintf(final, BUFSIZE*2, "%s %s%s,", mode, sjoin, cm->nick); else ircsnprintf(final, BUFSIZE*2, "%s%s,", sjoin, cm->nick); strlcat(chanusers, final, BUFSIZE*10); cmn = list_next (c->chanmembers, cmn); } return chanusers; } COLDEF neo_chanscols[] = { { "chans", "name", RTA_STR, CHANLEN, offsetof(struct Chans, name), RTA_READONLY, NULL, NULL, "The name of the channel" }, { "chans", "nomems", RTA_INT, sizeof(int), offsetof(struct Chans, cur_users), RTA_READONLY, NULL, NULL, "The no of users in the channel" }, { "chans", "modes", RTA_STR, BUFSIZE, offsetof(struct Chans, modes), RTA_READONLY, display_chanmodes, NULL, "The modes of the channel" }, { "chans", "users", RTA_STR, BUFSIZE*10, offsetof(struct Chans, chanmembers), RTA_READONLY, display_chanusers, NULL, "The users of the channel" }, { "chans", "topic", RTA_STR, BUFSIZE, offsetof(struct Chans, topic), RTA_READONLY, NULL, NULL, "The topic of the channel" }, { "chans", "topicowner", RTA_STR, MAXHOST, offsetof(struct Chans, topicowner), RTA_READONLY, NULL, NULL, "Who set the topic" }, { "chans", "topictime", RTA_INT, sizeof(int), offsetof(struct Chans, topictime), RTA_READONLY, NULL, NULL, "When the topic was set" }, { "chans", "created", RTA_INT, sizeof(int), offsetof(struct Chans, tstime), RTA_READONLY, NULL, NULL, "when the channel was created" }, { "chans", "flags", RTA_INT, sizeof(int), offsetof(struct Chans, flags), RTA_READONLY, NULL, NULL, "Flags for this channel" }, }; TBLDEF neo_chans = { "chans", NULL, /* for now */ sizeof(struct Chans), 0, TBL_HASH, neo_chanscols, sizeof(neo_chanscols) / sizeof(COLDEF), "", "The list of Channels on the IRC network" }; #endif /* SQLSRV */ /** @brief initialize the channel data * * initializes the channel data and channel hash ch. * * @return Nothing */ int init_chan_hash () { ch = hash_create (C_TABLE_SIZE, 0, 0); if(!ch) return NS_FAILURE; #ifdef SQLSRV /* add the server hash to the sql library */ neo_chans.address = ch; rta_add_table(&neo_chans); #endif return NS_SUCCESS; }