configure.in: Check for getpagesize(). Support --with-malloc=slab.
src/Makefile.am: Add alloc-slab.c as an extra source file.
src/alloc-srvx.c: Clean up srvx_free() slightly. Check for previously
allocated blocks.
srvx.conf.example, src/chanserv.c, src/main.c, src/modcmd.c,
src/proto-p10.c: Apply patches to bring closer to srvx-gs branch.
src/log.c: Assert and _exit() on fatal log messages.
src/opserv.c: Clarify logic for modes to set on join floods.
src/proto-common.c: Accept off-channel commands in registered channels.
git-archimport-id: srvx@srvx.net--2005-srvx/srvx--devo--1.3--patch-10
# arch-tag: automatic-ChangeLog--srvx@srvx.net--2005-srvx/srvx--devo--1.3
#
+2005-01-31 05:14:52 GMT Michael Poole <mdpoole@troilus.org> patch-10
+
+ Summary:
+ Add slab allocator; reduce delta with srvx-gs.
+ Revision:
+ srvx--devo--1.3--patch-10
+
+ configure.in: Check for getpagesize(). Support --with-malloc=slab.
+
+ src/Makefile.am: Add alloc-slab.c as an extra source file.
+
+ src/alloc-srvx.c: Clean up srvx_free() slightly. Check for previously
+ allocated blocks.
+
+ srvx.conf.example, src/chanserv.c, src/main.c, src/modcmd.c,
+ src/proto-p10.c: Apply patches to bring closer to srvx-gs branch.
+
+ src/log.c: Assert and _exit() on fatal log messages.
+
+ src/opserv.c: Clarify logic for modes to set on join floods.
+
+ src/proto-common.c: Accept off-channel commands in registered channels.
+
+ new files:
+ src/.arch-ids/alloc-slab.c.id src/alloc-slab.c
+
+ modified files:
+ ChangeLog configure.in src/Makefile.am src/alloc-srvx.c
+ src/chanserv.c src/chanserv.h src/common.h src/hash.h
+ src/helpfile.c src/log.c src/main.c src/modcmd.c src/opserv.c
+ src/proto-common.c src/proto-p10.c srvx.conf.example
+
+
2005-01-26 21:16:54 GMT Michael Poole <mdpoole@troilus.org> patch-9
Summary:
fi
dnl We have fallbacks in case these are missing, so just check for them.
-AC_CHECK_FUNCS(bcopy memcpy memset strdup strerror strsignal localtime_r setrlimit inet_ntoa getopt getopt_long regcomp regexec regfree sysconf,,)
+AC_CHECK_FUNCS(bcopy getpagesize memcpy memset strdup strerror strsignal localtime_r setrlimit inet_ntoa getopt getopt_long regcomp regexec regfree sysconf,,)
dnl Check for absolutely required library functions.
AC_CHECK_FUNCS(select socket strcspn strspn strtod strtoul,,AC_MSG_ERROR([a required function was not found. srvx build will fail.]))
AC_MSG_RESULT(srvx)
AC_DEFINE(WITH_MALLOC_SRVX, 1, [Define if using the srvx internal debug allocator])
MODULE_OBJS="$MODULE_OBJS alloc-srvx.\$(OBJEXT)"
+elif test "x$withval" = "xslab" ; then
+ AC_MSG_RESULT(slab)
+ AC_DEFINE(WITH_MALLOC_SLAB, 1, [Define if using the slab internal debug allocator])
+ MODULE_OBJS="$MODULE_OBJS alloc-slab.\$(OBJEXT)"
else
AC_MSG_ERROR([Unknown malloc type $withval])
fi
mv $$TMPFILE arch-version.h ; \
fi
-EXTRA_srvx_SOURCES = alloc-srvx.c proto-bahamut.c proto-common.c proto-p10.c mod-snoop.c mod-memoserv.c mod-helpserv.c mod-sockcheck.c
+EXTRA_srvx_SOURCES = alloc-slab.c alloc-srvx.c proto-bahamut.c proto-common.c proto-p10.c mod-snoop.c mod-memoserv.c mod-helpserv.c mod-sockcheck.c
srvx_LDADD = @MODULE_OBJS@
srvx_DEPENDENCIES = @MODULE_OBJS@
srvx_SOURCES = \
--- /dev/null
+/* alloc-slab.c - Slab debugging allocator
+ * Copyright 2005 srvx Development Team
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#include "common.h"
+#include "log.h"
+
+#if defined(HAVE_SYS_MMAN_H)
+# include <sys/mman.h>
+#endif
+
+#if !defined(HAVE_MMAP)
+# error The slab allocator requires that your system have the mmap() system call.
+#endif
+
+struct slab {
+ struct slabset *parent;
+ struct slab *prev;
+ struct slab *next;
+ void *base;
+ void **free;
+ unsigned int used;
+};
+
+struct slabset {
+ struct slabset *next;
+ struct slab *child;
+ size_t size;
+ size_t items_per_slab;
+};
+
+#define SLAB_MIN (2 * sizeof(void*))
+#define SLAB_GRAIN sizeof(void*)
+#define SLAB_ALIGN SLAB_GRAIN
+#define SMALL_CUTOFF 512
+/* Element size < SMALL_CUTOFF -> use small slabs.
+ * Larger elements are allocated directly using mmap(). The largest
+ * regularly allocated struct in srvx 1.x is smaller than
+ * SMALL_CUTOFF, so there is not much point in coding support for
+ * larger slabs.
+ */
+
+static struct slabset *little_slabs[SMALL_CUTOFF / SLAB_GRAIN];
+static struct slabset slabset_slabs;
+unsigned long alloc_count;
+unsigned long alloc_size;
+
+#if defined(MAP_ANON)
+#elif defined(MAP_ANONYMOUS)
+# define MAP_ANON MAP_ANONYMOUS
+#else
+# define MAP_ANON 0
+#endif
+
+static size_t
+slab_pagesize(void)
+{
+ static size_t pagesize;
+ if (pagesize
+#if defined(HAVE_GETPAGESIZE)
+ || (pagesize = getpagesize())
+#endif
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+ || (pagesize = sysconf(_SC_PAGESIZE))
+#endif
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
+ || (pagesize = sysconf(_SC_PAGE_SIZE))
+#endif
+ ) return pagesize;
+ assert(0 && "unable to find system page size");
+ return pagesize = 4096;
+}
+
+static size_t
+slab_round_up(size_t size)
+{
+ return (size + slab_pagesize() - 1) & ~(slab_pagesize() - 1);
+}
+
+static void *
+slab_map(size_t length)
+{
+ static int mmap_fd = -1;
+ void *res;
+
+#if ! MAP_ANON
+ if (mmap_fd < 0) {
+ mmap_fd = open("/dev/zero", 0);
+ if (mmap_fd < 0)
+ log_module(MAIN_LOG, LOG_FATAL, "Unable to open /dev/zero for mmap: %s", strerror(errno()));
+ }
+#endif
+ res = mmap(0, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, mmap_fd, 0);
+ if (res == MAP_FAILED)
+ log_module(MAIN_LOG, LOG_FATAL, "Unable to mmap %lu bytes (%s).", (unsigned long)length, strerror(errno));
+ return res;
+}
+
+static void *slab_alloc(struct slabset *sset);
+static void slab_unalloc(void *ptr, size_t size);
+
+static struct slabset *
+slabset_create(size_t size)
+{
+ unsigned int idx;
+
+ size = (size < SLAB_MIN) ? SLAB_MIN : (size + SLAB_GRAIN - 1) & ~(SLAB_GRAIN - 1);
+ idx = size / SLAB_GRAIN;
+ assert(idx < ArrayLength(little_slabs));
+ if (!little_slabs[idx]) {
+ if (!slabset_slabs.size) {
+ unsigned int idx2 = (sizeof(struct slabset) + SLAB_GRAIN - 1) / SLAB_GRAIN;
+ slabset_slabs.size = sizeof(struct slabset);
+ slabset_slabs.items_per_slab = (slab_pagesize() - sizeof(struct slab)) / ((sizeof(struct slabset) + SLAB_ALIGN - 1) & ~(SLAB_ALIGN - 1));
+ little_slabs[idx2] = &slabset_slabs;
+ if (idx == idx2)
+ return &slabset_slabs;
+ }
+ little_slabs[idx] = slab_alloc(&slabset_slabs);
+ little_slabs[idx]->size = size;
+ little_slabs[idx]->items_per_slab = (slab_pagesize() - sizeof(struct slab)) / ((size + SLAB_ALIGN - 1) & ~(SLAB_ALIGN - 1));
+ }
+ return little_slabs[idx];
+}
+
+static void *
+slab_alloc(struct slabset *sset)
+{
+ struct slab *slab;
+ void **item;
+
+ if (!sset->child || !sset->child->free) {
+ unsigned int ii, step;
+
+ /* Allocate new slab. */
+ item = slab_map(slab_pagesize());
+ slab = (struct slab*)((char*)item + slab_pagesize() - sizeof(*slab));
+ slab->base = item;
+
+ /* Populate free list. */
+ step = (sset->size + SLAB_ALIGN - 1) & ~(SLAB_ALIGN - 1);
+ for (ii = 1, item = slab->free = slab->base;
+ ii < sset->items_per_slab;
+ ++ii, item = (*item = (char*)item + step));
+
+ /* Link to parent slabset. */
+ slab->parent = sset;
+ if ((slab->prev = sset->child)) {
+ slab->next = slab->prev->next;
+ slab->prev->next = slab;
+ if (slab->next)
+ slab->next->prev = slab;
+ }
+ sset->child = slab;
+ }
+
+ slab = sset->child;
+ item = slab->free;
+ slab->free = *item;
+ if (slab->used++ == sset->items_per_slab) {
+ if (sset->child != slab) {
+ /* Unlink slab and reinsert before sset->child. */
+ if (slab->prev)
+ slab->prev->next = slab->next;
+ if (slab->next)
+ slab->next->prev = slab->prev;
+ if ((slab->prev = sset->child->prev))
+ slab->prev->next = slab;
+ if ((slab->next = sset->child))
+ slab->next->prev = slab;
+ } else if (slab->next) {
+ /* Advance sset->child to next pointer. */
+ sset->child = slab->next;
+ }
+ }
+ memset(item, 0, sset->size);
+ return item;
+}
+
+static void
+slab_unalloc(void *ptr, size_t size)
+{
+ void **item;
+ struct slab *slab, *new_next;
+
+ item = ptr;
+ assert(size < SMALL_CUTOFF);
+ slab = (struct slab*)((((unsigned long)ptr | (slab_pagesize() - 1)) + 1) - sizeof(*slab));
+ *item = slab->free;
+ slab->free = item;
+
+ if (slab->used-- == slab->parent->items_per_slab
+ && slab->parent->child != slab) {
+ new_next = slab->parent->child;
+ slab->parent->child = slab;
+ } else if (!slab->used) {
+ for (new_next = slab;
+ new_next->next && new_next->next->used;
+ new_next = new_next->next) ;
+ new_next = new_next->next;
+ } else
+ new_next = NULL;
+
+ if (new_next) {
+ if (slab->prev)
+ slab->prev->next = slab->next;
+ if (slab->next)
+ slab->next->prev = slab->prev;
+ if ((slab->prev = new_next->prev))
+ slab->prev->next = slab;
+ if ((slab->next = new_next->next))
+ slab->next->prev = slab;
+ }
+}
+
+void *
+slab_malloc(UNUSED_ARG(const char *file), UNUSED_ARG(unsigned int line), size_t size)
+{
+ size_t real, *res;
+
+ real = size + sizeof(size_t);
+ if (real < SMALL_CUTOFF)
+ res = slab_alloc(slabset_create(real));
+ else
+ res = slab_map(slab_round_up(real));
+ *res = size;
+ return res + 1;
+}
+
+void *
+slab_realloc(const char *file, unsigned int line, void *ptr, size_t size)
+{
+ size_t orig, *newblock;
+
+ if (!ptr)
+ return slab_malloc(file, line, size);
+
+ verify(ptr);
+ orig = ((size_t*)ptr)[-1];
+ if (orig >= size)
+ return ptr;
+ newblock = slab_malloc(file, line, size);
+ memcpy(newblock, ptr, orig);
+ return newblock;
+}
+
+char *
+slab_strdup(const char *file, unsigned int line, const char *src)
+{
+ char *target;
+ size_t len;
+
+ len = strlen(src) + 1;
+ target = slab_malloc(file, line, len);
+ memcpy(target, src, len);
+ return target;
+}
+
+void
+slab_free(UNUSED_ARG(const char *file), UNUSED_ARG(unsigned int line), void *ptr)
+{
+ size_t real, *size;
+
+ if (!ptr)
+ return;
+ verify(ptr);
+ size = (size_t*)ptr - 1;
+ real = *size + sizeof(size_t);
+ if (real < SMALL_CUTOFF)
+ slab_unalloc(size, real);
+ else
+ munmap(size, slab_round_up(real));
+}
+
+void
+verify(const void *ptr)
+{
+ size_t size;
+
+ if (!ptr)
+ return;
+ else if ((size = ((size_t*)ptr)[-1] + sizeof(size_t)) >= SMALL_CUTOFF)
+ assert(((unsigned long)ptr & (slab_pagesize() - 1)) == sizeof(size_t));
+ else {
+ struct slab *slab;
+ size_t expected;
+
+ expected = (size + SLAB_GRAIN - 1) & ~(SLAB_GRAIN - 1);
+ slab = (struct slab*)((((unsigned long)ptr | (slab_pagesize() - 1)) + 1) - sizeof(*slab));
+ assert(slab->parent->size == expected);
+ }
+}
*/
#include "common.h"
+#include "log.h"
#undef malloc
#undef free
block = malloc(sizeof(*block) + size + sizeof(redzone));
assert(block != NULL);
+ if (block->magic == ALLOC_MAGIC && block->file_id < file_ids_used) {
+ /* Only report the error, due to possible false positives. */
+ log_module(MAIN_LOG, LOG_WARNING, "Detected possible reallocation: %p (called by %s:%u/%u; allocated by %u:%u/%u).",
+ block, file, line, size, block->file_id, block->line, block->size);
+ }
memset(block, 0, sizeof(*block) + size);
memcpy((char*)(block + 1) + size, redzone, sizeof(redzone));
block->file_id = get_file_id(file);
}
void
-srvx_free(const char *file, unsigned int line, void *ptr)
+srvx_free(UNUSED_ARG(const char *file), UNUSED_ARG(unsigned int line), void *ptr)
{
struct alloc_header *block;
size_t size;
if (!ptr)
return;
+ verify(ptr);
block = (struct alloc_header *)ptr - 1;
- assert(block->magic == ALLOC_MAGIC);
- assert(0 == memcmp((char*)(block + 1) + block->size, redzone, sizeof(redzone)));
size = block->size;
memset(block + 1, 0xde, size);
block->magic = FREE_MAGIC;
free(block);
alloc_count--;
alloc_size -= size;
- (void)file; (void)line;
}
void
return 0;
}
- if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
+ if(!IsHelping(user)
+ && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
{
reply("CSMSG_MUST_BE_OPPED", channel->name);
return 0;
{
struct suspended *suspended = data;
struct chanNode *channel;
- struct mod_chanmode change;
if(!suspended->expires || (now < suspended->expires))
suspended->revoked = now;
channel = suspended->cData->channel;
suspended->cData->channel = channel;
suspended->cData->flags &= ~CHANNEL_SUSPENDED;
- mod_chanmode_init(&change);
- change.argc = 1;
- change.args[0].mode = MODE_CHANOP;
- change.args[0].u.member = AddChannelUser(chanserv, channel);
- mod_chanmode_announce(chanserv, channel, &change);
+ if(!IsOffChannel(suspended->cData))
+ {
+ struct mod_chanmode change;
+ mod_chanmode_init(&change);
+ change.argc = 1;
+ change.args[0].mode = MODE_CHANOP;
+ change.args[0].u.member = AddChannelUser(chanserv, channel);
+ mod_chanmode_announce(chanserv, channel, &change);
+ }
}
static CHANSERV_FUNC(cmd_csuspend)
new_owner = GetChannelAccess(cData, new_owner_hi);
if(!new_owner)
{
- reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
- return 0;
+ if(force)
+ {
+ new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
+ }
+ else
+ {
+ reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
+ return 0;
+ }
}
if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
{
s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
+ if (!reason || !owner)
+ return;
set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
{
char chan_name[CHANNELLEN+1];
char setter[NICKSERV_HANDLE_LEN+1];
- time_t set;
+ time_t set;
char reason[1];
};
extern void verify(const void *ptr);
# define verify(x) verify(x)
# endif
+#elif defined(WITH_MALLOC_SLAB)
+# define malloc(n) slab_malloc(__FILE__, __LINE__, (n))
+# undef calloc
+# define calloc(m,n) slab_malloc(__FILE__, __LINE__, (m)*(n))
+# undef realloc
+# define realloc(p,n) slab_realloc(__FILE__, __LINE__, (p), (n))
+# undef free
+# define free(p) slab_free(__FILE__, __LINE__, (p))
+# undef strdup
+# define strdup(s) slab_strdup(__FILE__, __LINE__, (s))
+extern void *slab_malloc(const char *, unsigned int, size_t);
+extern void *slab_realloc(const char *, unsigned int, void *, size_t);
+extern char *slab_strdup(const char *, unsigned int, const char *);
+extern void slab_free(const char *, unsigned int, void *);
+# if !defined(NDEBUG)
+extern void verify(const void *ptr);
+# define verify(x) verify(x)
+# endif
#endif
#ifndef verify
#define FLAGS_STAMPED 0x1000 /* for users who have been stamped */
#define FLAGS_HIDDEN_HOST 0x2000 /* user's host is masked by their account */
#define FLAGS_REGNICK 0x4000 /* user owns their current nick */
-#define FLAGS_REGISTERING 0x8000 /* user has issued account register command, is waiting for email cookie */
+#define FLAGS_REGISTERING 0x8000 /* user has issued account register command, is waiting for email cookie */
#define IsOper(x) ((x)->modes & FLAGS_OPER)
#define IsService(x) ((x)->modes & FLAGS_SERVICE)
unsigned int limit, locks;
char key[KEYLEN + 1];
time_t timestamp; /* creation time */
-
+
char topic[TOPICLEN + 1];
char topic_nick[NICKLEN + 1];
time_t topic_time;
* lang is a two-letter code according to ISO-639-1 (or three-letter
* code according to ISO-639-2 for languages not in ISO-639-1), and
* COUNTRY is the ISO 3166 country code in all upper case.
- *
+ *
* See also:
* http://www.loc.gov/standards/iso639-2/
* http://www.loc.gov/standards/iso639-2/langhome.html
/* Special behavior before we start full operation */
fprintf(stderr, "%s: %s\n", log_severity_names[sev], msgbuf);
}
+ if (sev == LOG_FATAL) {
+ assert(0 && "fatal message logged");
+ _exit(1);
+ }
}
/* audit log searching */
unsigned int matched = 0;
if (discrim->type) {
- struct logEntry *entry, *last;
+ volatile struct logEntry *last;
+ struct logEntry *entry;
for (entry = discrim->type->log_oldest, last = NULL;
entry;
FILE *file_out;
struct sigaction sv;
+#if WITH_MALLOC_BOEHM_GC
+ GC_find_leak = 1;
+ GC_set_warn_proc(gc_warn_proc);
+ GC_enable_incremental();
+#endif
+
daemon = 1;
debug = 0;
tools_init();
MAIN_LOG = log_register_type("srvx", "file:main.log");
if (debug)
log_debug();
-#if WITH_MALLOC_BOEHM_GC
- GC_set_warn_proc(gc_warn_proc);
- GC_enable_incremental();
-#endif
timeq_init();
init_structs();
init_parse();
{ "channel", MODCMD_REQUIRE_CHANNEL },
{ "chanuser", MODCMD_REQUIRE_CHANUSER },
{ "disabled", MODCMD_DISABLED },
+ { "helping", MODCMD_REQUIRE_HELPING },
{ "ignore_csuspend", MODCMD_IGNORE_CSUSPEND },
{ "joinable", MODCMD_REQUIRE_JOINABLE },
{ "keepbound", MODCMD_KEEP_BOUND },
{ "loghostmask", MODCMD_LOG_HOSTMASK },
- { "nolog", MODCMD_NO_LOG },
{ "networkhelper", MODCMD_REQUIRE_NETWORK_HELPER },
{ "never_csuspend", MODCMD_NEVER_CSUSPEND },
+ { "nolog", MODCMD_NO_LOG },
{ "oper", MODCMD_REQUIRE_OPER },
{ "qualified", MODCMD_REQUIRE_QUALIFIED },
{ "regchan", MODCMD_REQUIRE_REGCHAN },
{ "supporthelper", MODCMD_REQUIRE_SUPPORT_HELPER },
- { "helping", MODCMD_REQUIRE_HELPING },
{ "toy", MODCMD_TOY },
{ NULL, 0 }
};
rflags |= ACTION_STAFF;
}
if (cmd->min_opserv_level > 0) {
- if (!oper_has_access(user, bot, cmd->min_opserv_level, !(options & SVCCMD_NOISY))) return 0;
+ if (!oper_has_access(user, bot, cmd->min_opserv_level, !(options & SVCCMD_NOISY)))
+ return 0;
rflags |= ACTION_STAFF;
}
if (cmd->req_account_flags || cmd->deny_account_flags) {
/* If it's an override, return a special value. */
if ((flags & MODCMD_REQUIRE_CHANUSER)
&& (options & SVCCMD_NOISY)
- && (uData->access > 500)
+ && (!uData || (uData->access > 500))
&& (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
|| uData->access < cmd->min_channel_access)
&& !(flags & (MODCMD_REQUIRE_STAFF|MODCMD_REQUIRE_HELPING))) {
if (flags & MODCMD_REQUIRE_HELPING)
access = "helping";
else if (flags & MODCMD_REQUIRE_STAFF) {
- switch (flags & MODCMD_REQUIRE_STAFF) {
- case MODCMD_REQUIRE_OPER: access = "oper"; break;
- case MODCMD_REQUIRE_OPER | MODCMD_REQUIRE_NETWORK_HELPER:
- case MODCMD_REQUIRE_NETWORK_HELPER: access = "net.helper"; break;
- default: access = "staff"; break;
- }
+ if (flags & MODCMD_REQUIRE_OPER)
+ access = "oper";
+ else if (flags & MODCMD_REQUIRE_NETWORK_HELPER)
+ access = "net.helper";
+ else
+ access = "staff";
} else
access = strtab(svccmd->min_channel_access);
tbl.contents[ii+1][1+show_opserv_level] = access;
change.args[0].u.member = AddChannelUser(opserv, channel);
change.argc++;
}
- if (!(channel->modes & MODE_MODERATED))
- change.modes_set |= MODE_MODERATED;
+ change.modes_set = (MODE_MODERATED | MODE_DELAYJOINS) & ~channel->modes;
if (change.modes_set || change.argc)
mod_chanmode_announce(opserv, channel, &change);
send_target_message(0, channel->name, opserv, "OSMSG_FLOOD_MODERATE");
mn->idle_since = now;
/* Never send a NOTICE to a channel to one of the services */
- if (!pd->is_notice && cf->func && GetUserMode(cn, cf->service))
+ if (!pd->is_notice && cf->func
+ && ((cn->modes & MODE_REGISTERED) || GetUserMode(cn, cf->service)))
cf->func(pd->user, cn, pd->text+1, cf->service);
/* This catches *all* text sent to the channel that the services server sees */
for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
if (!cf->func)
- break; /* end of list */
+ break; /* end of list */
else
- cf->func(pd->user, cn, pd->text, cf->service);
+ cf->func(pd->user, cn, pd->text, cf->service);
}
}
dict_insert(irc_func_dict, "401", cmd_dummy); /* target left network */
dict_insert(irc_func_dict, "403", cmd_dummy); /* no such channel */
dict_insert(irc_func_dict, "404", cmd_dummy); /* cannot send to channel */
+ dict_insert(irc_func_dict, "439", cmd_dummy); /* target change too fast */
dict_insert(irc_func_dict, "441", cmd_dummy); /* target isn't on that channel */
dict_insert(irc_func_dict, "442", cmd_dummy); /* you aren't on that channel */
dict_insert(irc_func_dict, "443", cmd_dummy); /* is already on channel (after invite?) */
// How long is a channel unvisited (by masters or above) before it can be expired?
"chan_expire_delay" "30d";
// what !set options should we show when user calls "!set" with no arguments?
- "set_shows" ("DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes", "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUser", "CtcpReaction", "Protect", "Toys", "DynLimit", "NoDelete");
+ "set_shows" ("DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes", "PubCmd", "InviteMe", "StrictOp", "AutoOp", "EnfModes", "EnfTopic", "TopicSnarf", "UserInfo", "GiveVoice", "GiveOps", "EnfOps", "Setters", "CtcpUser", "CtcpReaction", "Protect", "Toys", "DynLimit", "NoDelete");
// A list of !8ball responses
"8ball" ("Not a chance.",