]> jfr.im git - irc/evilnet/x3.git/blobdiff - src/chanserv.c
Fixes to the ban expire stuff
[irc/evilnet/x3.git] / src / chanserv.c
index edf0d48ea8f8c255b176f84ca0f64d14813d1731..9f6b80cdd8c15550d098628ffb6be65b25c38bb9 100644 (file)
@@ -38,6 +38,7 @@
 #define KEY_ADJUST_DELAY       "adjust_delay"
 #define KEY_CHAN_EXPIRE_FREQ   "chan_expire_freq"
 #define KEY_CHAN_EXPIRE_DELAY  "chan_expire_delay"
+#define KEY_BAN_TIMEOUT_FREQ    "ban_timeout_freq"
 #define KEY_MAX_CHAN_USERS             "max_chan_users"
 #define KEY_MAX_CHAN_BANS      "max_chan_bans"
 #define KEY_NICK               "nick"
@@ -288,18 +289,19 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_SET_ENFTOPIC",      "$bEnfTopic    $b %d - and above can set the topic." },
     { "CSMSG_SET_PUBCMD",        "$bPubCmd      $b %d - and above can use public commands." },
     { "CSMSG_SET_SETTERS",       "$bSetters     $b %d - and above can change these settings." },
-/*    { "CSMSG_SET_CTCPUSERS",     "$bCTCPUsers   $b %d - and above can use ctcps." }, */
     { "CSMSG_SET_VOICE",         "$bvoice       $b %d - %s" },
     { "CSMSG_SET_PROTECT",       "$bProtect     $b %d - %s" },
     { "CSMSG_SET_TOYS",          "$bToys        $b %d - %s" },
     { "CSMSG_SET_CTCPREACTION",  "$bCTCPReaction$b %d - %s" },
     { "CSMSG_SET_TOPICREFRESH",  "$bTopicRefresh$b %d - %s" },
+    { "CSMSG_SET_BANTIMEOUT",    "$bBanTimeout  $b %d - %s" },
     { "CSMSG_USET_AUTOOP",       "$bAutoOp      $b %s" },
     { "CSMSG_USET_AUTOVOICE",    "$bAutoVoice   $b %s" },
     { "CSMSG_USET_AUTOINVITE",   "$bAutoInvite  $b %s" },
     { "CSMSG_USET_INFO",         "$bInfo        $b %s" },
 
     { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
+    { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
     { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
     { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
     { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
@@ -319,16 +321,25 @@ static const struct message_entry msgtab[] = {
     { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
     { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
     { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
+
     { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
     { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
     { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
     { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
     { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
+
     { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
     { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
     { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
     { "CSMSG_CTCPREACTION_SHORTBAN",  "Short timed ban on disallowed CTCPs" },
     { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
+    
+    { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
+    { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
+    { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
+    { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
+    { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
+    { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
 
     { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
     { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
@@ -524,6 +535,7 @@ static struct
 
     unsigned long      db_backup_frequency;
     unsigned long      channel_expire_frequency;
+    unsigned long       ban_timeout_frequency;
 
     long               info_delay;
     unsigned int       adjust_delay;
@@ -670,6 +682,13 @@ struct charOptionValues {
     { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
     { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
     { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
+}, banTimeoutValues[] = {
+    { '0', "CSMSG_BANTIMEOUT_NONE" },
+    { '1', "CSMSG_BANTIMEOUT_10M" },
+    { '2', "CSMSG_BANTIMEOUT_2H" },
+    { '3', "CSMSG_BANTIMEOUT_4H" },
+    { '4', "CSMSG_BANTIMEOUT_1D" },
+    { '5', "CSMSG_BANTIMEOUT_1W" }
 };
 
 static const struct {
@@ -684,7 +703,8 @@ static const struct {
     { "CSMSG_SET_PROTECT",      "protect",      'l', 0, ArrayLength(protectValues), protectValues },
     { "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_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues },
+    { "CSMSG_SET_BANTIMEOUT",   "bantimeout", '0', 11, ArrayLength(banTimeoutValues), banTimeoutValues }
 };
 
 struct userData *helperList;
@@ -3390,6 +3410,68 @@ find_matching_bans(struct banList *bans, struct userNode *actee, const char *mas
     return change;
 }
 
+void expire_bans(UNUSED_ARG(void* data)) /* Real bans, not lamers */
+{
+    unsigned int jj, ii, count;
+    struct banNode *bn;
+    struct chanData *channel;
+    time_t bantimeout;
+    struct mod_chanmode *change;
+
+    log_module(CS_LOG, LOG_DEBUG, "Checking for expired bans");
+    /* Walk through every channel */
+    for(channel = channelList; channel; channel = channel->next) {
+        switch(channel->chOpts[chBanTimeout])
+        {
+            default: case '0': continue; /* Dont remove bans in this chan */
+            case '1': bantimeout = now - (10 * 60);          break; /* 10 minutes */
+            case '2': bantimeout = now - (2 * 60 * 60);      break; /* 2 hours */
+            case '3': bantimeout = now - (4 * 60 * 60);      break; /* 4 hours */
+            case '4': bantimeout = now - (24 * 60 * 60);     break; /* 24 hours */
+            case '5': bantimeout = now - (7 * 24 * 60 * 60); break; /* 1 week */
+        }
+        count = 0;
+        /* First find out how many bans were going to unset */
+        for (jj=0; jj < channel->channel->banlist.used; ++jj) {
+            if(channel->channel->banlist.list[jj]->set < bantimeout)
+                count++;
+        }
+        if(count > 0) {
+            /* At least one ban, so setup a removal */
+            change = mod_chanmode_alloc(count);
+            ii = 0;
+            /* Walk over every ban in this channel.. */
+            for (jj=0; jj < channel->channel->banlist.used; ++jj) {
+                bn = channel->channel->banlist.list[jj];
+                if (bn->set < bantimeout) {
+                    log_module(CS_LOG, LOG_DEBUG, "Removing ban %s from %s", bn->ban, channel->channel->name);
+
+                    /* Add this ban to the mode change */
+                    change->args[ii].mode = MODE_REMOVE | MODE_BAN;
+                    change->args[ii].u.hostmask = strdup(bn->ban);
+                    ii++;
+                    /* Pull this ban out of the list */
+                    banList_remove(&(channel->channel->banlist), bn);
+                    jj--;
+                    free(bn);
+                }
+            }
+            /* Send the modes to IRC */
+            mod_chanmode_announce(chanserv, channel->channel, change);
+
+            /* free memory from strdup above */
+            for(ii = 0; ii < count; ++ii)
+                free((char*)change->args[ii].u.hostmask);
+
+            mod_chanmode_free(change);
+        }
+    } 
+    /* Set this function to run again */
+    if(chanserv_conf.ban_timeout_frequency)
+        timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
+}
+
+
 static int
 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
 {
@@ -5611,13 +5693,6 @@ static MODCMD_FUNC(chan_opt_setters)
     return channel_level_option(lvlSetters, CSFUNC_ARGS);
 }
 
-/*
-static MODCMD_FUNC(chan_opt_ctcpusers)
-{
-    return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
-}
-*/
-
 static MODCMD_FUNC(chan_opt_userinfo)
 {
     return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
@@ -5640,6 +5715,19 @@ static MODCMD_FUNC(chan_opt_inviteme)
     return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
 }
 
+/* TODO:  Make look like this when no args are 
+ *        given:
+ *  -X3- -------------------------------
+ *  -X3- BanTimeout: Bans are removed: 
+ *  -X3- ----- * indicates current -----
+ *  -X3-   0: [*] Never.
+ *  -X3-   1: [ ] After 10 minutes.
+ *  -X3-   2: [ ] After 2 hours.
+ *  -X3-   3: [ ] After 4 hours.
+ *  -X3-   4: [ ] After 24 hours.
+ *  -X3-   5: [ ] After one week.
+ *  -X3- ------------- End -------------
+ */
 static int
 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
 {
@@ -5700,6 +5788,11 @@ static MODCMD_FUNC(chan_opt_ctcpreaction)
     return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
 }
 
+static MODCMD_FUNC(chan_opt_bantimeout)
+{
+    return channel_multiple_option(chBanTimeout, CSFUNC_ARGS);
+}
+
 static MODCMD_FUNC(chan_opt_topicrefresh)
 {
     return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
@@ -6172,6 +6265,7 @@ static CHANSERV_FUNC(cmd_wut)
     return 1;
 }
 
+#ifdef lame8ball
 static CHANSERV_FUNC(cmd_8ball)
 {
     unsigned int i, j, accum;
@@ -6194,6 +6288,169 @@ static CHANSERV_FUNC(cmd_8ball)
     return 1;
 }
 
+#else /* Use cool 8ball instead */
+
+void eightball(char *outcome, int method, unsigned int seed)
+{
+   int answer = 0;
+
+#define NUMOFCOLORS 18
+   char ballcolors[50][50] = {"blue", "red", "green", "yellow",
+        "white", "black", "grey", "brown",
+        "yellow", "pink", "purple", "orange", "teal", "burgandy",
+        "fuchsia","turquoise","magenta", "cyan"};
+#define NUMOFLOCATIONS 50
+   char balllocations[50][55] = { 
+       "Locke's house", "Oregon", "California", "Indiana", "Canada",
+        "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
+        "the Caribbean", "the Everglades", "your head", "your pants", "your school",
+        "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
+        "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
+        "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
+        "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
+        "your bra", "your hair", "your bed", "the couch", "the wall", 
+       "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands", 
+       "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
+#define NUMOFPREPS 15
+   char ballpreps[50][50] = { 
+       "Near", "Somewhere near", "In", "In", "In", 
+       "In", "Hiding in", "Under", "Next to", "Over", 
+       "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
+#define NUMOFNUMS 34
+   char ballnums[50][50] = { 
+        "A hundred", "A thousand", "A few", "42",
+        "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
+        "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
+        "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
+        };
+#define NUMOFMULTS 8
+   char ballmults[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
+
+   /* Method:
+    * 0: normal  (Not used in x3)
+    * 1: color
+    * 2: where is
+    * 3: how many
+    */
+
+    srand(seed);
+    if (method == 1) /* A Color */
+    {
+      char tmp[MAXLEN];
+
+      answer = (rand() % 12); /* Make sure this is the # of entries */
+      switch(answer)
+      {
+        case 0: strcpy(tmp, "Very bright %s, I'd say.");
+                break;
+        case 1: strcpy(tmp, "Sort of a light %s color.");
+                break;
+        case 2: strcpy(tmp, "Dark and dreary %s.");
+                break;
+        case 3: strcpy(tmp, "Quite a pale shade of %s.");
+                break;
+        case 4: strcpy(tmp, "A gross kind of mucky %s.");
+                break;
+        case 5: strcpy(tmp, "Brilliant whiteish %s.");
+               break;
+       case 6: case 7: case 8: case 9: strcpy(tmp, "%s.");
+               break;
+       case 10: strcpy(tmp, "Solid %s.");
+               break;
+       case 11: strcpy(tmp, "Transparent %s.");
+                break;
+        default: strcpy(outcome, "An invalid random number was generated.");
+                return;
+      }
+      sprintf(outcome, tmp, ballcolors[rand() % NUMOFCOLORS]);
+      return;
+    }
+    else if (method == 2)  /* Location */
+    {
+       sprintf(outcome, "%s %s.", ballpreps[rand() % NUMOFPREPS], balllocations[rand() % NUMOFLOCATIONS]);
+    }
+    else if (method == 3)  /* Number of ___ */
+    {
+       sprintf(outcome, "%s%s.", ballnums[rand() % NUMOFNUMS], ballmults[rand() % NUMOFMULTS]);
+    }
+    else
+    {
+      //Debug(DBGWARNING, "Error in 8ball.");
+    }
+    return;
+}
+
+static CHANSERV_FUNC(cmd_8ball)
+{
+  char *word1, *word2, *word3;
+  static char eb[MAXLEN];
+  unsigned int accum, i, j;
+
+  REQUIRE_PARAMS(2);
+  accum = 0;
+  for(i=1; i<argc; i++)
+    for(j=0; argv[i][j]; j++)
+      accum = (accum << 5) - accum + toupper(argv[i][j]);
+
+  accum += time(NULL)/3600;
+  word1 = argv[1];
+  word2 = argc>2?argv[2]:"";
+  word3 = argc>3?argv[3]:"";
+
+/*** COLOR *****/
+  if((word2) && strcasecmp(word1, "what") == 0 && strcasecmp(word2, "color") == 0)
+     eightball(eb, 1, accum);
+  else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
+     eightball(eb, 1, accum);
+  else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
+     eightball(eb, 1, accum);
+/*** LOCATION *****/
+  else if(
+           (
+             word2 &&
+             (
+                (strcasecmp(word1, "where") == 0) &&
+                (strcasecmp(word2, "is") == 0)
+             )
+           ) ||
+             (
+                   strcasecmp(word1, "where's") == 0
+             )
+         )
+     eightball(eb, 2, accum);
+/*** NUMBER *****/
+  else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0)
+     eightball(eb, 3, accum);
+/*** GENERIC *****/
+  else
+  {
+     /* Generic 8ball question.. so pull from x3.conf srvx style */
+           const char *resp;
+
+           resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
+           if(channel)
+           {
+               char response[MAXLEN];
+               sprintf(response, "\002%s\002: %s", user->nick, resp);
+               irc_privmsg(cmd->parent->bot, channel->name, response);
+           }
+           else
+               send_message_type(4, user, cmd->parent->bot, "%s", resp);
+           return 1;
+  }
+
+  if(channel)
+  {
+        char response[MAXLEN];
+        sprintf(response, "\002%s\002: %s", user->nick, eb);
+        irc_privmsg(cmd->parent->bot, channel->name, response);
+  }
+  else
+        send_message_type(4, user, cmd->parent->bot, "%s", eb);
+  return 1;
+}
+#endif
+
 static CHANSERV_FUNC(cmd_d)
 {
     unsigned long sides, count, modifier, ii, total;
@@ -6653,7 +6910,7 @@ handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *c
 
     if(protect_user(victim, kicker, channel->channel_info))
     {
-        const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
+        const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_KICK");
        KickChannelUser(kicker, channel, chanserv, reason);
     }
 
@@ -6922,6 +7179,8 @@ chanserv_conf_read(void)
     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
+    str = database_get_data(conf_node, KEY_BAN_TIMEOUT_FREQ, RECDB_QSTRING);
+    chanserv_conf.ban_timeout_frequency = str ? ParseInterval(str) : 600;
     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
@@ -6976,7 +7235,7 @@ chanserv_conf_read(void)
             /* multiple choice options */
             "CtcpReaction", "Protect", "Toys", "TopicRefresh",
             /* binary options */
-            "DynLimit", "NoDelete",
+            "DynLimit", "NoDelete", "BanTimeout",
             /* delimiter */
             NULL
         };
@@ -7855,8 +8114,8 @@ init_chanserv(const char *nick)
     DEFINE_CHANNEL_OPTION(toys);
     DEFINE_CHANNEL_OPTION(setters);
     DEFINE_CHANNEL_OPTION(topicrefresh);
-//    DEFINE_CHANNEL_OPTION(ctcpusers);
     DEFINE_CHANNEL_OPTION(ctcpreaction);
+    DEFINE_CHANNEL_OPTION(bantimeout);
     DEFINE_CHANNEL_OPTION(inviteme);
     if(off_channel > 1)
         DEFINE_CHANNEL_OPTION(offchannel);
@@ -7888,6 +8147,9 @@ init_chanserv(const char *nick)
     if(chanserv_conf.channel_expire_frequency)
        timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
 
+    if(chanserv_conf.ban_timeout_frequency)
+        timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
+
     if(chanserv_conf.refresh_period)
     {
         time_t next_refresh;