/* hash.c - IRC network state database
* Copyright 2000-2004 srvx Development Team
*
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of srvx.
+ *
+ * srvx 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. Important limitations are
- * listed in the COPYING file that accompanies this software.
+ * (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
* 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, email srvx-maintainers@srvx.net.
+ * along with srvx; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
#include "conf.h"
dict_t clients;
dict_t servers;
unsigned int max_clients, invis_clients;
-time_t max_clients_time;
+unsigned long max_clients_time;
struct userList curr_opers;
static void hash_cleanup(void);
reg_exit_func(hash_cleanup);
}
+int userList_contains(struct userList *list, struct userNode *user)
+{
+ unsigned int ii;
+
+ for (ii = 0; ii < list->used; ++ii) {
+ if (user == list->list[ii]) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
server_link_func_t *slf_list;
unsigned int slf_size = 0, slf_used = 0;
reg_server_link_func(server_link_func_t handler)
{
if (slf_used == slf_size) {
- if (slf_size) {
- slf_size <<= 1;
- slf_list = realloc(slf_list, slf_size*sizeof(server_link_func_t));
- } else {
- slf_size = 8;
- slf_list = malloc(slf_size*sizeof(server_link_func_t));
- }
+ if (slf_size) {
+ slf_size <<= 1;
+ slf_list = realloc(slf_list, slf_size*sizeof(server_link_func_t));
+ } else {
+ slf_size = 8;
+ slf_list = malloc(slf_size*sizeof(server_link_func_t));
+ }
}
slf_list[slf_used++] = handler;
}
reg_new_user_func(new_user_func_t handler)
{
if (nuf_used == nuf_size) {
- if (nuf_size) {
- nuf_size <<= 1;
- nuf_list = realloc(nuf_list, nuf_size*sizeof(new_user_func_t));
- } else {
- nuf_size = 8;
- nuf_list = malloc(nuf_size*sizeof(new_user_func_t));
- }
+ if (nuf_size) {
+ nuf_size <<= 1;
+ nuf_list = realloc(nuf_list, nuf_size*sizeof(new_user_func_t));
+ } else {
+ nuf_size = 8;
+ nuf_list = malloc(nuf_size*sizeof(new_user_func_t));
+ }
}
nuf_list[nuf_used++] = handler;
}
reg_del_user_func(del_user_func_t handler)
{
if (duf_used == duf_size) {
- if (duf_size) {
- duf_size <<= 1;
- duf_list = realloc(duf_list, duf_size*sizeof(del_user_func_t));
- } else {
- duf_size = 8;
- duf_list = malloc(duf_size*sizeof(del_user_func_t));
- }
+ if (duf_size) {
+ duf_size <<= 1;
+ duf_list = realloc(duf_list, duf_size*sizeof(del_user_func_t));
+ } else {
+ duf_size = 8;
+ duf_list = malloc(duf_size*sizeof(del_user_func_t));
+ }
}
duf_list[duf_used++] = handler;
}
{
struct mod_chanmode change;
unsigned int n;
-
+
irc_user(user);
mod_chanmode_init(&change);
change.argc = 1;
for (n = 0; n < user->channels.used; n++) {
struct modeNode *mn = user->channels.list[n];
- irc_join(user, mn->channel);
+ irc_join(user, mn->channel);
if (mn->modes) {
change.args[0].mode = mn->modes;
- change.args[0].member = mn;
+ change.args[0].u.member = mn;
mod_chanmode_announce(user, mn->channel, &change);
}
}
#endif
/* Make callbacks for nick changes. Do this with new nick in
- * place because that is slightly more useful.
+ * place because that is slightly more useful. Stop if the user
+ * gets killed by any of the hooks, so that later hooks do not get
+ * confused by the user having disappeared.
*/
- for (nn=0; nn<ncf2_used; nn++)
+ for (nn=0; (nn<ncf2_used) && !user->dead; nn++)
ncf2_list[nn](user, old_nick);
user->timestamp = now;
if (IsLocal(user) && !no_announce)
}
void
-call_account_func(struct userNode *user, const char *stamp)
+call_account_func(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
{
/* We've received an account stamp for a user; notify
NickServ, which registers the sole account_func
P10 Protocol violation if (user->modes & FLAGS_STAMPED) here.
*/
if (account_func)
- account_func(user, stamp);
+ account_func(user, stamp, timestamp, serial);
#ifdef WITH_PROTOCOL_P10
/* Mark the user so we don't stamp it again. */
}
void
-StampUser(struct userNode *user, const char *stamp)
+StampUser(struct userNode *user, const char *stamp, unsigned long timestamp, unsigned long serial)
{
#ifdef WITH_PROTOCOL_P10
/* The P10 protocol says we can't stamp users who already
return;
#endif
- irc_account(user, stamp);
+ irc_account(user, stamp, timestamp, serial);
user->modes |= FLAGS_STAMPED;
}
+void
+assign_fakehost(struct userNode *user, const char *host, const char *ident, int force, int announce)
+{
+ if (host)
+ safestrncpy(user->fakehost, host, sizeof(user->fakehost));
+ if (ident)
+ safestrncpy(user->fakeident, ident, sizeof(user->ident));
+ if (announce)
+ irc_fakehost(user, host, ident, force);
+}
+
static new_channel_func_t *ncf_list;
static unsigned int ncf_size = 0, ncf_used = 0;
reg_new_channel_func(new_channel_func_t handler)
{
if (ncf_used == ncf_size) {
- if (ncf_size) {
- ncf_size <<= 1;
- ncf_list = realloc(ncf_list, ncf_size*sizeof(ncf_list[0]));
- } else {
- ncf_size = 8;
- ncf_list = malloc(ncf_size*sizeof(ncf_list[0]));
- }
+ if (ncf_size) {
+ ncf_size <<= 1;
+ ncf_list = realloc(ncf_list, ncf_size*sizeof(ncf_list[0]));
+ } else {
+ ncf_size = 8;
+ ncf_list = malloc(ncf_size*sizeof(ncf_list[0]));
+ }
}
ncf_list[ncf_used++] = handler;
}
reg_join_func(join_func_t handler)
{
if (jf_used == jf_size) {
- if (jf_size) {
- jf_size <<= 1;
- jf_list = realloc(jf_list, jf_size*sizeof(join_func_t));
- } else {
- jf_size = 8;
- jf_list = malloc(jf_size*sizeof(join_func_t));
- }
+ if (jf_size) {
+ jf_size <<= 1;
+ jf_list = realloc(jf_list, jf_size*sizeof(join_func_t));
+ } else {
+ jf_size = 8;
+ jf_list = malloc(jf_size*sizeof(join_func_t));
+ }
}
jf_list[jf_used++] = handler;
}
int rel_age;
static void
-wipeout_channel(struct chanNode *cNode, time_t new_time, char **modes, unsigned int modec) {
+wipeout_channel(struct chanNode *cNode, unsigned long new_time, char **modes, unsigned int modec) {
unsigned int orig_limit;
chan_mode_t orig_modes;
char orig_key[KEYLEN+1];
+ char orig_apass[KEYLEN+1];
+ char orig_upass[KEYLEN+1];
unsigned int nn, argc;
/* nuke old topic */
orig_modes = cNode->modes;
orig_limit = cNode->limit;
strcpy(orig_key, cNode->key);
+ strcpy(orig_upass, cNode->upass);
+ strcpy(orig_apass, cNode->apass);
cNode->modes = 0;
mod_chanmode(NULL, cNode, modes, modec, 0);
cNode->timestamp = new_time;
argc++;
}
if (argc) {
- extern struct userNode *opserv;
struct mod_chanmode *change;
change = mod_chanmode_alloc(argc);
change->modes_set = orig_modes;
change->new_limit = orig_limit;
strcpy(change->new_key, orig_key);
+ strcpy(change->new_upass, orig_upass);
+ strcpy(change->new_apass, orig_apass);
for (nn = argc = 0; nn < cNode->members.used; ++nn) {
struct modeNode *mn = cNode->members.list[nn];
if ((mn->modes & MODE_CHANOP) && IsService(mn->user) && IsLocal(mn->user)) {
change->args[argc].mode = MODE_CHANOP;
- change->args[argc].member = mn;
+ change->args[argc].u.member = mn;
argc++;
}
}
assert(argc == change->argc);
- if (change->argc > 0)
- mod_chanmode_announce(change->args[0].member->user, cNode, change);
- else
- mod_chanmode_announce(opserv, cNode, change);
+ change->args[0].u.member->modes &= ~MODE_CHANOP;
+ mod_chanmode_announce(change->args[0].u.member->user, cNode, change);
mod_chanmode_free(change);
}
}
struct chanNode *
-AddChannel(const char *name, time_t time_, const char *modes, char *banlist)
+AddChannel(const char *name, unsigned long time_, const char *modes, char *banlist)
{
struct chanNode *cNode;
char new_modes[MAXLEN], *argv[MAXNUMPARAMS];
strcpy(cNode->name, name);
banList_init(&cNode->banlist);
modeList_init(&cNode->members);
- mod_chanmode(NULL, cNode, argv, nn, 0);
+ userList_init(&cNode->invited);
+ mod_chanmode(NULL, cNode, argv, nn, MCP_FROM_SERVER);
dict_insert(channels, cNode->name, cNode);
cNode->timestamp = time_;
rel_age = 1;
wipeout_channel(cNode, time_, argv, nn);
rel_age = 1;
} else if (cNode->timestamp == time_) {
- mod_chanmode(NULL, cNode, argv, nn, 0);
+ mod_chanmode(NULL, cNode, argv, nn, MCP_FROM_SERVER);
rel_age = 0;
} else {
rel_age = -1;
reg_del_channel_func(del_channel_func_t handler)
{
if (dcf_used == dcf_size) {
- if (dcf_size) {
- dcf_size <<= 1;
- dcf_list = realloc(dcf_list, dcf_size*sizeof(dcf_list[0]));
- } else {
- dcf_size = 8;
- dcf_list = malloc(dcf_size*sizeof(dcf_list[0]));
- }
+ if (dcf_size) {
+ dcf_size <<= 1;
+ dcf_list = realloc(dcf_list, dcf_size*sizeof(dcf_list[0]));
+ } else {
+ dcf_size = 8;
+ dcf_list = malloc(dcf_size*sizeof(dcf_list[0]));
+ }
}
dcf_list[dcf_used++] = handler;
}
{
unsigned int n;
+ verify(channel);
dict_remove(channels, channel->name);
if (channel->members.used || channel->locks) {
/* go through all channel members and delete them from the channel */
for (n=channel->members.used; n>0; )
- DelChannelUser(channel->members.list[--n]->user, channel, false, 1);
+ DelChannelUser(channel->members.list[--n]->user, channel, NULL, 1);
/* delete all channel bans */
for (n=channel->banlist.used; n>0; )
modeList_clean(&channel->members);
banList_clean(&channel->banlist);
+ userList_clean(&channel->invited);
free(channel);
}
struct modeNode *
AddChannelUser(struct userNode *user, struct chanNode* channel)
{
- struct modeNode *mNode;
- unsigned int n;
+ struct modeNode *mNode;
+ unsigned int n;
- mNode = GetUserMode(channel, user);
- if (mNode)
+ mNode = GetUserMode(channel, user);
+ if (mNode)
return mNode;
- mNode = malloc(sizeof(*mNode));
+ mNode = malloc(sizeof(*mNode));
- /* set up modeNode */
- mNode->channel = channel;
- mNode->user = user;
- mNode->modes = 0;
+ /* set up modeNode */
+ mNode->channel = channel;
+ mNode->user = user;
+ mNode->modes = 0;
+ mNode->oplevel = MAXOPLEVEL;
mNode->idle_since = now;
- /* Add modeNode to channel and to user.
+ /* Add modeNode to channel and to user.
* We have to do this before calling join funcs in case the
* modeNode is manipulated (e.g. chanserv ops the user).
*/
- modeList_append(&channel->members, mNode);
- modeList_append(&user->channels, mNode);
+ modeList_append(&channel->members, mNode);
+ modeList_append(&user->channels, mNode);
- if (channel->members.used == 1)
+ if (channel->members.used == 1
+ && !(channel->modes & MODE_REGISTERED)
+ && !(channel->modes & MODE_APASS))
mNode->modes |= MODE_CHANOP;
- for (n=0; n<jf_used; n++) {
+ if (IsLocal(user)) {
+ irc_join(user, channel);
+ }
+
+ for (n=0; (n<jf_used) && !user->dead; n++) {
/* Callbacks return true if they kick or kill the user,
* and we can continue without removing mNode. */
if (jf_list[n](mNode))
return NULL;
}
- if (IsLocal(user))
- irc_join(user, channel);
+ return mNode;
+}
+
+/* Return negative if *(struct modeNode**)pa is "less than" pb,
+ * positive if pa is "larger than" pb. Comparison is based on sorting
+ * so that non-voiced/non-opped users are first, voiced-only users are
+ * next, and the "strongest" oplevels are before "weaker" oplevels.
+ * Within those sets, ordering is arbitrary.
+ */
+int
+modeNode_sort(const void *pa, const void *pb)
+{
+ struct modeNode *a = *(struct modeNode**)pa;
+ struct modeNode *b = *(struct modeNode**)pb;
+
+ if (a->modes & MODE_CHANOP) {
+ if (!(b->modes & MODE_CHANOP))
+ return -1;
+ else if ((b->modes & MODE_VOICE) != (a->modes & MODE_VOICE))
+ return (b->modes & MODE_VOICE) - (a->modes & MODE_VOICE);
+ else if (a->oplevel != b->oplevel)
+ return a->oplevel - b->oplevel;
+ } else if (b->modes & MODE_CHANOP)
+ return 1;
+ else if ((b->modes & MODE_VOICE) != (a->modes & MODE_VOICE))
+ return (b->modes & MODE_VOICE) - (a->modes & MODE_VOICE);
- return mNode;
+ return irccasecmp(a->user->nick, b->user->nick);
}
static part_func_t *pf_list;
reg_part_func(part_func_t handler)
{
if (pf_used == pf_size) {
- if (pf_size) {
- pf_size <<= 1;
- pf_list = realloc(pf_list, pf_size*sizeof(part_func_t));
- } else {
- pf_size = 8;
- pf_list = malloc(pf_size*sizeof(part_func_t));
- }
+ if (pf_size) {
+ pf_size <<= 1;
+ pf_list = realloc(pf_list, pf_size*sizeof(part_func_t));
+ } else {
+ pf_size = 8;
+ pf_list = malloc(pf_size*sizeof(part_func_t));
+ }
}
pf_list[pf_used++] = handler;
}
struct modeNode* mNode;
unsigned int n;
- if (reason) {
+ if (IsLocal(user) && reason)
irc_part(user, channel, reason);
- }
mNode = GetUserMode(channel, user);
/* remove modeNode from channel and user */
modeList_remove(&channel->members, mNode);
modeList_remove(&user->channels, mNode);
- free(mNode);
+ /* make callbacks */
for (n=0; n<pf_used; n++)
- pf_list[n](user, channel, reason);
+ pf_list[n](mNode, reason);
- if (!deleting && !channel->members.used && !channel->locks)
+ /* free memory */
+ free(mNode);
+
+ /* A single check for APASS only should be enough here */
+ if (!deleting && !channel->members.used && !channel->locks
+ && !(channel->modes & MODE_REGISTERED) && !(channel->modes & MODE_APASS))
DelChannel(channel);
}
if (IsLocal(target))
{
- /* NULL reason because we don't want a PART message to be
- sent by DelChannelUser. */
- DelChannelUser(target, channel, NULL, 0);
+ /* NULL reason because we don't want a PART message to be
+ sent by DelChannelUser. */
+ DelChannelUser(target, channel, NULL, 0);
}
}
reg_kick_func(kick_func_t handler)
{
if (kf_used == kf_size) {
- if (kf_size) {
- kf_size <<= 1;
- kf_list = realloc(kf_list, kf_size*sizeof(kick_func_t));
- } else {
- kf_size = 8;
- kf_list = malloc(kf_size*sizeof(kick_func_t));
- }
+ if (kf_size) {
+ kf_size <<= 1;
+ kf_list = realloc(kf_list, kf_size*sizeof(kick_func_t));
+ } else {
+ kf_size = 8;
+ kf_list = malloc(kf_size*sizeof(kick_func_t));
+ }
}
kf_list[kf_used++] = handler;
}
unsigned int n;
struct modeNode *mn;
- if (!victim || !channel || IsService(victim) || !GetUserMode(channel, victim))
+ if (!victim || !channel || !GetUserMode(channel, victim))
return;
-
+
/* Update the kicker's idle time (kicker may be null if it was a server) */
if (kicker && (mn = GetUserMode(channel, kicker)))
mn->idle_since = now;
for (n=0; n<kf_used; n++)
- kf_list[n](kicker, victim, channel);
+ kf_list[n](kicker, victim, channel);
DelChannelUser(victim, channel, 0, 0);
if (IsLocal(victim))
- irc_part(victim, channel, NULL);
+ irc_part(victim, channel, NULL);
}
int ChannelBanExists(struct chanNode *channel, const char *ban)
unsigned int n;
for (n = 0; n < channel->banlist.used; n++)
- if (match_ircglobs(channel->banlist.list[n]->ban, ban))
- return 1;
+ if (match_ircglobs(channel->banlist.list[n]->ban, ban))
+ return 1;
return 0;
}
reg_topic_func(topic_func_t handler)
{
if (tf_used == tf_size) {
- if (tf_size) {
- tf_size <<= 1;
- tf_list = realloc(tf_list, tf_size*sizeof(topic_func_t));
- } else {
- tf_size = 8;
- tf_list = malloc(tf_size*sizeof(topic_func_t));
- }
+ if (tf_size) {
+ tf_size <<= 1;
+ tf_list = realloc(tf_list, tf_size*sizeof(topic_func_t));
+ } else {
+ tf_size = 8;
+ tf_list = malloc(tf_size*sizeof(topic_func_t));
+ }
}
tf_list[tf_used++] = handler;
}
}
if (announce) {
- /* We don't really care if a local user messes with the topic,
+ /* We don't really care if a local user messes with the topic,
* so don't call the tf_list functions. */
- irc_topic(user, channel, topic);
+ irc_topic(user, channel, topic);
} else {
- for (n=0; n<tf_used; n++)
- if (tf_list[n](user, channel, old_topic))
+ for (n=0; n<tf_used; n++)
+ /* A topic change handler can return non-zero to indicate
+ * that it has reverted the topic change, and that further
+ * hooks should not be called.
+ */
+ if (tf_list[n](user, channel, old_topic))
break;
}
}
{
unsigned int n;
struct modeNode *mn = NULL;
+
+ verify(channel);
+ verify(channel->members.list);
+ verify(user);
+ verify(user->channels.list);
if (channel->members.used < user->channels.used) {
- for (n=0; n<channel->members.used; n++) {
- if (user == channel->members.list[n]->user) {
- mn = channel->members.list[n];
- break;
- }
- }
+ for (n=0; n<channel->members.used; n++) {
+ verify(channel->members.list[n]);
+ if (user == channel->members.list[n]->user) {
+ mn = channel->members.list[n];
+ break;
+ }
+ }
} else {
- for (n=0; n<user->channels.used; n++) {
- if (channel == user->channels.list[n]->channel) {
- mn = user->channels.list[n];
- break;
- }
- }
+ for (n=0; n<user->channels.used; n++) {
+ verify(user->channels.list[n]);
+ if (channel == user->channels.list[n]->channel) {
+ mn = user->channels.list[n];
+ break;
+ }
+ }
}
return mn;
}
static void
hash_cleanup(void)
{
+ dict_iterator_t it, next;
+
DelServer(self, 0, NULL);
+ for (it = dict_first(channels); it; it = next) {
+ next = iter_next(it);
+ DelChannel(iter_data(it));
+ }
dict_delete(channels);
dict_delete(clients);
dict_delete(servers);