]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/chanserv.c
Changed/improved 'silent' gline support
[irc/evilnet/x3.git] / src / chanserv.c
index 7eb19737df4fa53ce693bc7374bd669234e89723..e963adec541af791d61ded98c37d15bc7b64077e 100644 (file)
@@ -24,6 +24,7 @@
 #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"
 
@@ -287,6 +290,8 @@ static const struct message_entry msgtab[] = {
     { "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" },
@@ -327,6 +332,12 @@ static const struct message_entry msgtab[] = {
     { "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" },
@@ -340,6 +351,16 @@ static const struct message_entry msgtab[] = {
     { "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." },
@@ -361,6 +382,8 @@ static const struct message_entry msgtab[] = {
     { "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." },
@@ -533,10 +556,13 @@ static int eject_user(struct userNode *user, struct chanNode *channel, unsigned
 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
 {
@@ -698,6 +724,22 @@ struct charOptionValues {
     { '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 {
@@ -713,7 +755,9 @@ 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;
@@ -724,7 +768,6 @@ unsigned int chanserv_read_version = 0; /* db version control */
 
 #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)
 
@@ -1269,8 +1312,10 @@ del_channel_user(struct userData *user, int do_gc)
 
     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* 
@@ -1388,7 +1433,7 @@ expire_adduser_pending()
 
 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;
@@ -1599,6 +1644,7 @@ expire_channels(UNUSED_ARG(void *data))
 
         /* 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.");
     }
 
@@ -2141,11 +2187,39 @@ static CHANSERV_FUNC(cmd_unregister)
     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;
@@ -2154,6 +2228,7 @@ static CHANSERV_FUNC(cmd_move)
     struct userData *uData;
     char reason[MAXLEN];
     struct do_not_register *dnr;
+    int chanserv_join = 0, spamserv_join;
 
     REQUIRE_PARAMS(2);
 
@@ -2195,7 +2270,7 @@ static CHANSERV_FUNC(cmd_move)
     {
         target = AddChannel(argv[1], now, NULL, NULL, NULL);
         if(!IsSuspended(channel->channel_info))
-            AddChannelUser(chanserv, target);
+            chanserv_join = 1;
     }
     else if(target->channel_info)
     {
@@ -2209,12 +2284,7 @@ static CHANSERV_FUNC(cmd_move)
         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)
     {
@@ -2234,7 +2304,10 @@ static CHANSERV_FUNC(cmd_move)
     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))
@@ -2246,6 +2319,7 @@ static CHANSERV_FUNC(cmd_move)
     UnlockChannel(channel);
     LockChannel(target);
     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
+    reply("CSMSG_MOVE_SUCCESS", target->name);
     return 1;
 }
 
@@ -2479,6 +2553,7 @@ static CHANSERV_FUNC(cmd_merge)
 
     /* 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);
@@ -3061,13 +3136,113 @@ bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban
     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;
@@ -3118,7 +3293,17 @@ eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, c
            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
@@ -3927,6 +4112,22 @@ advanced_list(struct listData *list)
         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
@@ -3974,6 +4175,9 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg
             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; 
@@ -4005,9 +4209,27 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg
         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 && 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++;
@@ -4020,9 +4242,27 @@ cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int arg
         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 && 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";
@@ -4728,7 +4968,8 @@ static MODCMD_FUNC(cmd_wipeinfo)
     return 1;
 }
 
-static CHANSERV_FUNC(cmd_resync)
+static void
+resync_channel(struct chanNode *channel)
 {
     struct mod_chanmode *changes;
     struct chanData *cData = channel->channel_info;
@@ -4870,8 +5111,13 @@ static CHANSERV_FUNC(cmd_resync)
         }
     }
     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;
 }
@@ -5201,12 +5447,8 @@ chanserv_expire_suspension(void *data)
     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);
     }
 }
 
@@ -5281,6 +5523,7 @@ static CHANSERV_FUNC(cmd_csuspend)
 
         /* 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);
@@ -5968,6 +6211,16 @@ static MODCMD_FUNC(chan_opt_topicrefresh)
     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
@@ -6453,6 +6706,25 @@ chanserv_refresh_topics(UNUSED_ARG(void *data))
     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)
@@ -6830,6 +7102,7 @@ handle_join(struct modeNode *mNode)
     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
@@ -6853,6 +7126,7 @@ handle_join(struct modeNode *mNode)
             }
         }
     }
+#endif
 
     mod_chanmode_init(&change);
     change.argc = 1;
@@ -6996,7 +7270,7 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
 {
     struct mod_chanmode change;
     struct userData *channel;
-    unsigned int ii, jj;
+    unsigned int ii, jj, i;
 
     if(!user->handle_info)
        return;
@@ -7086,6 +7360,15 @@ handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
             }
         }
     }
+
+    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
@@ -7445,6 +7728,8 @@ chanserv_conf_read(void)
     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";
@@ -7470,6 +7755,7 @@ chanserv_conf_read(void)
             "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", 
             /* multiple choice options */
             "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh",
+            "Resync", "BanType"
             /* binary options */
             "DynLimit", "NoDelete", "BanTimeout",
             /* delimiter */
@@ -8260,6 +8546,8 @@ chanserv_db_cleanup(void) {
 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);
 
@@ -8413,6 +8701,8 @@ init_chanserv(const char *nick)
     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);
@@ -8454,6 +8744,14 @@ init_chanserv(const char *nick)
         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);