*
* This file is part of x3.
*
- * srvx is free software; you can redistribute it and/or modify
+ * x3 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.
#include "modcmd.h"
#include "opserv.h" /* for opserv_bad_channel() */
#include "saxdb.h"
+#include "spamserv.h"
#include "timeq.h"
#define CHANSERV_CONF_NAME "services/chanserv"
#define KEY_EXPIRES "expires"
#define KEY_TRIGGERED "triggered"
+#define KEY_GOD_TIMEOUT "god_timeout"
+
#define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
#define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
{ "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
{ "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
{ "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
+ { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
+ { "CSMSG_SET_BANTYPE", "$bBanType $b %d - %s" },
{ "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
{ "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
{ "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
{ "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
+ { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
+ { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
+ { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
+ { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
+ { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
+
{ "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
{ "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
{ "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
{ "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
{ "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
+ { "CSMSG_BANTYPE_A", "*!user@host" },
+ { "CSMSG_BANTYPE_B", "*!*user@host" },
+ { "CSMSG_BANTYPE_C", "*!*@host" },
+ { "CSMSG_BANTYPE_D", "*!*user@*.host" },
+ { "CSMSG_BANTYPE_E", "*!*@*.host" },
+ { "CSMSG_BANTYPE_F", "nick!user@host" },
+ { "CSMSG_BANTYPE_G", "nick!*@host" },
+ { "CSMSG_BANTYPE_H", "nick!*user@*.host" },
+ { "CSMSG_BANTYPE_I", "nick!*@*.host" },
+
{ "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
{ "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
{ "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
{ "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
{ "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
{ "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
+ { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
+ { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
*/
{ "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
{ "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
{ "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
{ "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
{ "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
+ { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
{ "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
{ "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
struct userNode *chanserv;
dict_t note_types;
int off_channel;
+extern struct string_list *autojoin_channels;
static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
static struct log_type *CS_LOG;
struct adduserPending* adduser_pendings = NULL;
+extern const char *hidden_host_suffix;
unsigned int adduser_pendings_count = 0;
+unsigned long god_timeout;
static struct
{
{ '3', "CSMSG_BANTIMEOUT_4H" },
{ '4', "CSMSG_BANTIMEOUT_1D" },
{ '5', "CSMSG_BANTIMEOUT_1W" }
+}, resyncValues[] = {
+ { 'n', "CSMSG_RESYNC_NEVER" },
+ { '1', "CSMSG_RESYNC_3_HOURS" },
+ { '2', "CSMSG_RESYNC_6_HOURS" },
+ { '3', "CSMSG_RESYNC_12_HOURS" },
+ { '4', "CSMSG_RESYNC_24_HOURS" }
+}, banTypeValues[] = {
+ { '1', "CSMSG_BANTYPE_A" },
+ { '2', "CSMSG_BANTYPE_B" },
+ { '3', "CSMSG_BANTYPE_C" },
+ { '4', "CSMSG_BANTYPE_D" },
+ { '5', "CSMSG_BANTYPE_E" },
+ { '6', "CSMSG_BANTYPE_F" },
+ { '7', "CSMSG_BANTYPE_G" },
+ { '8', "CSMSG_BANTYPE_H" },
+ { '9', "CSMSG_BANTYPE_I" }
};
static const struct {
{ "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
{ "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
{ "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues },
- { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues), banTimeoutValues }
+ { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues), banTimeoutValues },
+ { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues), resyncValues },
+ { "CSMSG_SET_BANTYPE", "bantype", '4', 13, ArrayLength(banTypeValues), banTypeValues },
};
struct userData *helperList;
#define CHANSERV_DB_VERSION 2
-#define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
#define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
#define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
free(user->info);
free(user);
- if(do_gc && !channel->users && !IsProtected(channel))
+ if(do_gc && !channel->users && !IsProtected(channel)) {
+ spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
unregister_channel(channel, "lost all users.");
+ }
}
static struct adduserPending*
static void expire_ban(void *data);
-static struct banData*
+struct banData*
add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
{
struct banData *bd;
/* Unregister the channel */
log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
+ spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
unregister_channel(channel, "registration expired.");
}
dict_iterator_t it;
reply("CSMSG_DNR_SEARCH_RESULTS");
- reply("CSMSG_BAR");
+ if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
+ reply("CSMSG_BAR");
matches = 0;
for(it = dict_first(handle_dnrs); it; it = iter_next(it))
{
}
reply("CSMSG_DNR_SEARCH_RESULTS");
- reply("CSMSG_BAR");
+ if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
+ reply("CSMSG_BAR");
if(*target == '*')
matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
else
sprintf(reason, "unregistered by %s.", user->handle_info->handle);
name = strdup(channel->name);
unregister_channel(cData, reason);
+ spamserv_cs_unregister(user, channel, manually, "unregistered");
reply("CSMSG_UNREG_SUCCESS", name);
free(name);
return 1;
}
+static void
+ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
+{
+ extern struct userNode *spamserv;
+ struct mod_chanmode *change;
+
+ if(spamserv && spamserv_join && get_chanInfo(channel->name))
+ {
+ change = mod_chanmode_alloc(2);
+ change->argc = 2;
+ change->args[0].mode = MODE_CHANOP;
+ change->args[0].u.member = AddChannelUser(chanserv, channel);
+ change->args[1].mode = MODE_CHANOP;
+ change->args[1].u.member = AddChannelUser(spamserv, channel);
+ }
+ else
+ {
+ change = mod_chanmode_alloc(1);
+ change->argc = 1;
+ change->args[0].mode = MODE_CHANOP;
+ change->args[0].u.member = AddChannelUser(chanserv, channel);
+ }
+
+ mod_chanmode_announce(chanserv, channel, change);
+ mod_chanmode_free(change);
+}
+
static CHANSERV_FUNC(cmd_move)
{
struct mod_chanmode change;
struct userData *uData;
char reason[MAXLEN];
struct do_not_register *dnr;
+ int chanserv_join = 0, spamserv_join;
REQUIRE_PARAMS(2);
{
target = AddChannel(argv[1], now, NULL, NULL, NULL);
if(!IsSuspended(channel->channel_info))
- AddChannelUser(chanserv, target);
+ chanserv_join = 1;
}
else if(target->channel_info)
{
return 0;
}
else if(!IsSuspended(channel->channel_info))
- {
- change.argc = 1;
- change.args[0].mode = MODE_CHANOP;
- change.args[0].u.member = AddChannelUser(chanserv, target);
- mod_chanmode_announce(chanserv, target, &change);
- }
+ chanserv_join = 1;
if(off_channel > 0)
{
target->channel_info->channel = target;
channel->channel_info = NULL;
- reply("CSMSG_MOVE_SUCCESS", target->name);
+ spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
+
+ if (chanserv_join)
+ ss_cs_join_channel(target, spamserv_join);
sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
if(!IsSuspended(target->channel_info))
UnlockChannel(channel);
LockChannel(target);
global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
+ reply("CSMSG_MOVE_SUCCESS", target->name);
return 1;
}
/* Merge the channel structures and associated data. */
merge_channel(channel->channel_info, target->channel_info);
+ spamserv_cs_move_merge(user, channel, target, 0);
sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
unregister_channel(channel->channel_info, reason);
reply("CSMSG_MERGE_SUCCESS", target->name);
}
static int
-cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
+cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
{
struct userData *actor, *uData, *next;
char interval[INTERVALLEN];
{
next = uData->next;
- if((uData->seen > limit) || uData->present)
+ if((uData->seen > limit)
+ || uData->present
+ || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
continue;
if(((uData->access >= min_access) && (uData->access <= max_access))
{
unsigned long duration;
unsigned short min_level, max_level;
+ int vacation;
REQUIRE_PARAMS(3);
+ vacation = argc > 3 && !strcmp(argv[3], "vacation");
duration = ParseInterval(argv[2]);
if(duration < 60)
{
}
else if(!irccasecmp(argv[1], "users"))
{
- cmd_trim_users(user, channel, 0, 0, duration);
+ cmd_trim_users(user, channel, 0, 0, duration, vacation);
return 1;
}
else if(parse_level_range(&min_level, &max_level, argv[1]))
{
- cmd_trim_users(user, channel, min_level, max_level, duration);
+ cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
return 1;
}
else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
{
- cmd_trim_users(user, channel, min_level, min_level, duration);
+ cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
return 1;
}
else
if(IsService(mn->user))
continue;
- if(!user_matches_glob(mn->user, ban, 1))
+ if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
continue;
if(protect_user(mn->user, user, channel->channel_info))
return 0;
}
+#define i_isdigit(x) isdigit((int) (unsigned char) (x))
+
+int is_ipv4_address(const char *host)
+{
+ while (*host != '\0') {
+ if (*host != '.' && !i_isdigit(*host))
+ return 0;
+ host++;
+ }
+ return 1;
+}
+
+static char *get_domain_mask(char *host)
+{
+ char *ptr;
+
+ if (strchr(host, '.') == NULL) {
+ /* no dots - toplevel domain or IPv6 address */
+ ptr = strrchr(host, ':');
+ if (ptr != NULL) {
+ /* IPv6 address, ban the last 64k addresses */
+ if (ptr[1] != '\0') strcpy(ptr+1, "*");
+ }
+ return host;
+ }
+
+ if (is_ipv4_address(host)) {
+ /* it's an IP address, change last digit to * */
+ ptr = strrchr(host, '.');
+ if (ptr != NULL && i_isdigit(ptr[1]))
+ strcpy(ptr+1, "*");
+ } else {
+ /* if more than one dot, skip the first
+ (dyn123.blah.net -> *.blah.net) */
+ ptr = strchr(host, '.');
+ if (ptr != NULL && strchr(ptr+1, '.') != NULL) {
+ host = ptr-1;
+ host[0] = '*';
+ }
+ }
+ return host;
+}
+
+char *generate_ban_hostmask(struct userNode *user, const char banopt)
+{
+ char *nickname = NULL;
+ char *ident = "*";
+ char *hostname = NULL;
+ char *mask = NULL;
+ char *usemask = NULL;
+ int len;
+
+ usemask = user->hostname;
+ if (IsFakeHost(user) && IsHiddenHost(user))
+ usemask = user->fakehost;
+ else if (IsSetHost(user))
+ usemask = strchr(user->sethost, '@') + 1;
+ else if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix) {
+ usemask = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
+ sprintf(usemask, "%s.%s", user->handle_info->handle, hidden_host_suffix);
+ }
+
+ if((banopt == '6') || (banopt == '7') || (banopt == '8') || (banopt == '9'))
+ nickname = user->nick;
+ else
+ nickname = "*";
+
+ if((banopt == '4') || (banopt == '5') || (banopt == '8') || (banopt == '9'))
+ hostname = get_domain_mask(usemask);
+ else
+ hostname = usemask;
+
+ if((banopt == '1') || (banopt == '6')) {
+ if (IsSetHost(user)) {
+ ident = alloca(strcspn(user->sethost, "@")+2);
+ safestrncpy(ident, user->sethost, strcspn(user->sethost, "@")+1);
+ } else
+ ident = user->ident;
+ }
+ else if((banopt == '2') || (banopt == '4') || (banopt == '8')) {
+ if (IsSetHost(user)) {
+ ident = alloca(strcspn(user->sethost, "@")+3);
+ ident[0] = '*';
+ safestrncpy(ident+1, user->sethost, strcspn(user->sethost, "@")+1);
+ } else {
+ ident = malloc(strlen(user->ident)+1);
+ sprintf(ident, "*%s", user->ident);
+ }
+ } else
+ ident = "*";
+
+ /* Put it all together. */
+ len = strlen(ident) + strlen(hostname) + strlen(nickname) + 3;
+ mask = malloc(len);
+ sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
+
+ return mask;
+}
+
static int
eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
{
struct userNode *victim;
struct modeNode **victims;
+ struct chanData *cData;
unsigned int offset, n, victimCount, duration = 0;
- char *reason = "Bye.", *ban, *name;
+ char *reason = "Bye.", *ban, *name, banopt;
char interval[INTERVALLEN];
offset = (action & ACTION_ADD_TIMED_LAMER) ? 3 : 2;
return 0;
}
- ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
+ if(!(cData = channel->channel_info)) {
+ banopt = '4';
+ }
+
+ if (!(cData->chOpts[chBanType])) {
+ banopt = '4';
+ } else {
+ banopt = cData->chOpts[chBanType];
+ }
+
+ ban = generate_ban_hostmask(victim, banopt);
name = victim->nick;
}
else
{
for(ii = count = 0; ii < bans->used; ++ii)
{
- match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
+ match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
+ MATCH_USENICK | MATCH_VISIBLE);
if(match[ii])
count++;
}
while(ban)
{
if(actee)
- for( ; ban && !user_matches_glob(actee, ban->mask, 1);
+ for( ; ban && !user_matches_glob(actee, ban->mask, MATCH_USENICK | MATCH_VISIBLE);
ban = ban->next);
else
for( ; ban && !match_ircglobs(mask, ban->mask);
table_send(list->bot, list->user->nick, 0, NULL, list->table);
}
+static void
+classic_list(struct listData *list)
+{
+ const char *msg;
+ if(list->search)
+ send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
+ else
+ send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
+ if(list->table.length == 1)
+ {
+ msg = user_find_message(list->user, "MSG_NONE");
+ send_message_type(4, list->user, list->bot, " %s", msg);
+ }
+ else
+ table_send(list->bot, list->user->nick, 0, NULL, list->table);
+}
*/
static int
case HI_STYLE_ADVANCED:
send_list = advanced_list;
break;
+ case HI_STYLE_CLASSIC:
+ send_list = classic_list;
+ break;
case HI_STYLE_NORMAL:
default:
send_list = normal_list;
lData.table.flags = TABLE_NO_FREE;
lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
- if(user->handle_info->userlist_style == HI_STYLE_ADVANCED)
+ if(user->handle_info && user->handle_info->userlist_style == HI_STYLE_ADVANCED)
lData.table.width = 5; /* with level = 5 */
else
lData.table.width = 4; /* without = 4 */
ary = malloc(lData.table.width*sizeof(**lData.table.contents));
lData.table.contents[0] = ary;
- ary[i++] = "Access";
- if(user->handle_info->userlist_style == HI_STYLE_ADVANCED)
- ary[i++] = "Level"; /* Only on advanced view */
+ if(user->handle_info) {
+ switch(user->handle_info->userlist_style) {
+ case HI_STYLE_CLASSIC:
+ ary[i++] = "Level";
+ break;
+ case HI_STYLE_ADVANCED:
+ ary[i++] = "Access";
+ ary[i++] = "Level";
+ break;
+ case HI_STYLE_CLEAN:
+ ary[i++] = "Access";
+ break;
+ case HI_STYLE_NORMAL:
+ default:
+ ary[i++] = "Access";
+ break;
+ }
+ }
+ else {
+ ary[i++] = "Access";
+ }
ary[i++] = "Account";
ary[i] = "Last Seen";
seen_index = i++;
i = 0;
ary = malloc(lData.table.width*sizeof(**lData.table.contents));
lData.table.contents[matches] = ary;
- ary[i++] = user_level_name_from_level(uData->access);
- if(user->handle_info->userlist_style == HI_STYLE_ADVANCED)
- ary[i++] = strtab(uData->access);
+ if(user->handle_info) {
+ switch(user->handle_info->userlist_style) {
+ case HI_STYLE_CLASSIC:
+ ary[i++] = strtab(uData->access);
+ break;
+ case HI_STYLE_ADVANCED:
+ ary[i++] = user_level_name_from_level(uData->access);
+ ary[i++] = strtab(uData->access);
+ break;
+ case HI_STYLE_CLEAN:
+ ary[i++] = user_level_name_from_level(uData->access);
+ break;
+ case HI_STYLE_NORMAL:
+ default:
+ ary[i++] = user_level_name_from_level(uData->access);
+ break;
+ }
+ }
+ else {
+ ary[i++] = user_level_name_from_level(uData->access);
+ }
ary[i++] = uData->handle->handle;
if(uData->present)
ary[i] = "Here";
{
struct adduserPending *ap;
reply("CSMSG_ADDUSER_PENDING_HEADER");
- reply("CSMSG_BAR");
+ if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
+ reply("CSMSG_BAR");
for(ap = adduser_pendings;ap;ap = ap->next)
reply("CSMSG_ADDUSER_PENDING_LIST", ap->channel->name, ap->user->nick);
reply("CSMSG_ADDUSER_PENDING_FOOTER");
static CHANSERV_FUNC(cmd_mode)
{
+ struct userData *uData;
struct mod_chanmode *change;
+ short base_oplevel;
if(argc < 2)
{
return 1;
}
- change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
+ uData = GetChannelUser(channel->channel_info, user->handle_info);
+ if (!uData)
+ base_oplevel = MAXOPLEVEL;
+ else if (uData->access >= UL_OWNER)
+ base_oplevel = 1;
+ else
+ base_oplevel = 1 + UL_OWNER - uData->access;
+ change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
+
if(!change)
{
reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
cData = channel->channel_info;
reply("CSMSG_CHANNEL_INFO", channel->name);
- reply("CSMSG_BAR");
+ if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
+ reply("CSMSG_BAR");
uData = GetChannelUser(cData, user->handle_info);
if(uData && (uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/))
for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
show_giveownership_info(cmd, user, giveownership);
}
- reply("CSMSG_CHANNEL_END");
+ if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
+ reply("CSMSG_CHANNEL_END");
+ else
+ reply("CSMSG_CHANNEL_END_CLEAN");
return 1;
}
irc_make_chanmode(channel, modes);
reply("CSMSG_PEEK_INFO", channel->name);
- reply("CSMSG_BAR");
+ if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
+ reply("CSMSG_BAR");
reply("CSMSG_PEEK_TOPIC", channel->topic);
reply("CSMSG_PEEK_MODES", modes);
reply("CSMSG_PEEK_USERS", channel->members.used);
return 1;
}
-static CHANSERV_FUNC(cmd_resync)
+static void
+resync_channel(struct chanNode *channel)
{
struct mod_chanmode *changes;
struct chanData *cData = channel->channel_info;
}
}
changes->argc = used;
- modcmd_chanmode_announce(changes);
+ mod_chanmode_announce(chanserv, channel, changes);
mod_chanmode_free(changes);
+}
+
+static CHANSERV_FUNC(cmd_resync)
+{
+ resync_channel(channel);
reply("CSMSG_RESYNCED_USERS", channel->name);
return 1;
}
report.reporter = chanserv;
report.user = user;
reply("CSMSG_EVENT_SEARCH_RESULTS", channel->name);
- reply("CSMSG_BAR");
+ if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
+ reply("CSMSG_BAR");
matches = log_entry_search(&discrim, log_report_entry, &report);
if(matches)
reply("MSG_MATCH_COUNT", matches);
suspended->cData->flags &= ~CHANNEL_SUSPENDED;
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);
+ spamserv_cs_suspend(channel, 0, 0, NULL);
+ ss_cs_join_channel(channel, 1);
}
}
/* Mark the channel as suspended, then part. */
channel->channel_info->flags |= CHANNEL_SUSPENDED;
+ spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
DelChannelUser(chanserv, channel, suspended->reason, 0);
reply("CSMSG_SUSPENDED", channel->name);
sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
if(action == search_print)
{
- reply("CSMSG_CHANNEL_SEARCH_RESULTS");
- reply("CSMSG_BAR");
+ reply("CSMSG_CHANNEL_SEARCH_RESULTS");
+ if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
+ reply("CSMSG_BAR");
}
matches = chanserv_channel_search(search, action, user);
{
memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
}
- else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
+ else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1,MCP_KEY_FREE|MCP_REGISTERED, 0)))
{
reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
return 0;
return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
}
+static MODCMD_FUNC(chan_opt_resync)
+{
+ return channel_multiple_option(chResync, CSFUNC_ARGS);
+}
+
+static MODCMD_FUNC(chan_opt_bantype)
+{
+ return channel_multiple_option(chBanType, CSFUNC_ARGS);
+}
+
static struct svccmd_list set_shows_list;
static void
if(argc < 2)
{
- reply("CSMSG_CHANNEL_OPTIONS", channel->name);
- reply("CSMSG_BAR");
+ reply("CSMSG_CHANNEL_OPTIONS", channel->name);
+ if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
+ reply("CSMSG_BAR");
for(ii = 0; ii < set_shows_list.used; ii++)
{
subcmd = set_shows_list.list[ii];
timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
}
+static void
+chanserv_auto_resync(UNUSED_ARG(void *data))
+{
+ unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
+ struct chanData *cData;
+ char opt;
+
+ for(cData = channelList; cData; cData = cData->next)
+ {
+ if(IsSuspended(cData)) continue;
+ opt = cData->chOpts[chResync];
+ if(opt == 'n') continue;
+ if((refresh_num - cData->last_resync) < (unsigned int)(1 << (opt - '1'))) continue;
+ resync_channel(cData->channel);
+ cData->last_resync = refresh_num;
+ }
+ timeq_add(now + chanserv_conf.refresh_period, chanserv_auto_resync, NULL);
+}
+
static CHANSERV_FUNC(cmd_unf)
{
if(channel)
if(channel->members.used > cData->max)
cData->max = channel->members.used;
+#ifdef notdef
+ /* Check for bans. If they're joining through a ban, one of two
+ * cases applies:
+ * 1: Join during a netburst, by riding the break. Kick them
+ * unless they have ops or voice in the channel.
+ * 2: They're allowed to join through the ban (an invite in
+ * ircu2.10, or a +e on Hybrid, or something).
+ * If they're not joining through a ban, and the banlist is not
+ * full, see if they're on the banlist for the channel. If so,
+ * kickban them.
+ */
+ if(user->uplink->burst && !mNode->modes)
+ {
+ unsigned int ii;
+ for(ii = 0; ii < channel->banlist.used; ii++)
+ {
+ if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
+ {
+ /* Riding a netburst. Naughty. */
+ KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
+ return 1;
+ }
+ }
+ }
+#endif
+
mod_chanmode_init(&change);
change.argc = 1;
if(channel->banlist.used < MAXBANS)
{
/* Not joining through a ban. */
for(bData = cData->bans;
- bData && !user_matches_glob(user, bData->mask, 1);
- bData = bData->next);
+ bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
+ bData = bData->next);
if(bData)
{
uData->present = 1;
}
}
+
+ /* If user joining normally (not during burst), apply op or voice,
+ * and send greeting/userinfo as appropriate.
+ */
if(!user->uplink->burst)
{
if(modes)
change.args[0].u.member = mNode;
mod_chanmode_announce(chanserv, channel, &change);
}
- if(greeting && !user->uplink->burst)
+ if(greeting)
send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
if(uData && info)
send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
{
struct mod_chanmode change;
struct userData *channel;
- unsigned int ii, jj;
+ unsigned int ii, jj, i;
if(!user->handle_info)
return;
|| IsSuspended(channel->channel_info))
continue;
for(jj = 0; jj < channel->banlist.used; ++jj)
- if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
+ if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
break;
if(jj < channel->banlist.used)
continue;
for(ban = channel->channel_info->bans; ban; ban = ban->next)
{
char kick_reason[MAXLEN];
- if(!user_matches_glob(user, ban->mask, 1))
+ if(!user_matches_glob(user, ban->mask,MATCH_USENICK | MATCH_VISIBLE))
continue;
change.args[0].mode = MODE_BAN;
change.args[0].u.hostmask = ban->mask;
}
}
}
+
+ if (user->handle_info->ignores->used) {
+ for (i=0; i < user->handle_info->ignores->used; i++) {
+ irc_silence(user, user->handle_info->ignores->list[i], 1);
+ }
+ }
+
+ if (user->handle_info->epithet)
+ irc_swhois(chanserv, user, user->handle_info->epithet);
}
static void
continue;
/* Look for a matching ban already on the channel. */
for(jj = 0; jj < channel->banlist.used; ++jj)
- if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
+ if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
break;
/* Need not act if we found one. */
if(jj < channel->banlist.used)
/* Look for a matching ban in this channel. */
for(bData = channel->channel_info->bans; bData; bData = bData->next)
{
- if(!user_matches_glob(user, bData->mask, 1))
+ if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
continue;
change.args[0].u.hostmask = bData->mask;
mod_chanmode_announce(chanserv, channel, &change);
chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
+ str = database_get_data(conf_node, KEY_GOD_TIMEOUT, RECDB_QSTRING);
+ god_timeout = str ? ParseInterval(str) : 60*15;
str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
if(!str)
str = "+nt";
safestrncpy(mode_line, str, sizeof(mode_line));
ii = split_line(mode_line, 0, ArrayLength(modes), modes);
- if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
+ if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
+ && (change->argc < 2))
{
chanserv_conf.default_modes = *change;
mod_chanmode_free(change);
"EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
/* multiple choice options */
"AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh",
+ "Resync", "BanType"
/* binary options */
"DynLimit", "NoDelete", "BanTimeout",
/* delimiter */
if(!IsSuspended(cData)
&& (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
&& (argc = split_line(str, 0, ArrayLength(argv), argv))
- && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
+ && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
cData->modes = *modes;
if(off_channel > 0)
cData->modes.modes_set |= MODE_REGISTERED;
void
init_chanserv(const char *nick)
{
+ struct chanNode *chan;
+ unsigned int i;
CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
conf_register_reload(chanserv_conf_read);
DEFINE_CHANNEL_OPTION(toys);
DEFINE_CHANNEL_OPTION(setters);
DEFINE_CHANNEL_OPTION(topicrefresh);
+ DEFINE_CHANNEL_OPTION(resync);
+ DEFINE_CHANNEL_OPTION(bantype);
DEFINE_CHANNEL_OPTION(ctcpreaction);
DEFINE_CHANNEL_OPTION(bantimeout);
DEFINE_CHANNEL_OPTION(inviteme);
time_t next_refresh;
next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
timeq_add(next_refresh, chanserv_refresh_topics, NULL);
+ timeq_add(next_refresh, chanserv_auto_resync, NULL);
+ }
+
+ if (autojoin_channels && chanserv) {
+ for (i = 0; i < autojoin_channels->used; i++) {
+ chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
+ AddChannelUser(chanserv, chan)->modes |= MODE_CHANOP;
+ }
}
reg_exit_func(chanserv_db_cleanup);