Do not eat first word in ?part reason.
[srvx.git] / src / opserv.c
index 3eb44033e7a515ac5f6068b92bc8de16ad86aa76..92cee32c2d9dfdb25084492517f399d2a4120143 100644 (file)
@@ -252,6 +252,7 @@ static const struct message_entry msgtab[] = {
     { "OSMSG_CHANINFO_USER_COUNT", "Users (%d):" },
     { "OSMSG_CSEARCH_CHANNEL_INFO", "%s [%d users] %s %s" },
     { "OSMSG_TRACE_MAX_CHANNELS", "You may not use the 'channel' criterion more than %d times." },
+    { "OSMSG_FORCEKICK_LOCAL", "You cannot kick $b%s$b forcefully." },
     { NULL, NULL }
 };
 
@@ -327,7 +328,7 @@ opserv_free_hostinfo(void *data)
 typedef struct opservDiscrim {
     struct chanNode *channels[DISCRIM_MAX_CHANS];
     unsigned int channel_count;
-    char *mask_nick, *mask_ident, *mask_host, *mask_info, *server, *reason, *accountmask;
+    char *mask_nick, *mask_ident, *mask_host, *mask_info, *server, *reason, *notice_target, *accountmask;
     irc_in_addr_t ip_mask;
     unsigned long limit;
     time_t min_ts, max_ts;
@@ -381,6 +382,7 @@ opserv_free_user_alert(void *data)
 
 #define opserv_debug(format...) do { if (opserv_conf.debug_channel) send_channel_notice(opserv_conf.debug_channel , opserv , ## format); } while (0)
 #define opserv_alert(format...) do { if (opserv_conf.alert_channel) send_channel_notice(opserv_conf.alert_channel , opserv , ## format); } while (0)
+#define opserv_custom_alert(chan, format...) do { if (chan) send_target_message(4 , chan , opserv , ## format); else if (opserv_conf.alert_channel) send_channel_notice(opserv_conf.alert_channel , opserv , ## format); } while (0)
 
 /* A lot of these commands are very similar to what ChanServ can do,
  * but OpServ can do them even on channels that aren't registered.
@@ -806,7 +808,7 @@ static MODCMD_FUNC(cmd_block)
         offset = 3;
     }
     if(duration && duration != opserv_conf.block_gline_duration) {
-        // We require more access when the duration is not the default block duration.
+        /* We require more access when the duration is not the default block duration. */
         gline_cmd = dict_find(cmd->parent->commands, "gline", NULL);
         if(!gline_cmd)
         {
@@ -1005,6 +1007,34 @@ static MODCMD_FUNC(cmd_kick)
     return 1;
 }
 
+static MODCMD_FUNC(cmd_forcekick)
+{
+    struct userNode *target;
+    char *reason;
+
+    if (argc < 3) {
+        reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1);
+        sprintf(reason, OSMSG_KICK_REQUESTED, user->nick);
+    } else {
+        reason = unsplit_string(argv+2, argc-2, NULL);
+    }
+    target = GetUserH(argv[1]);
+    if (!target) {
+        reply("MSG_NICK_UNKNOWN", argv[1]);
+        return 0;
+    }
+    if (!GetUserMode(channel, target)) {
+        reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name);
+        return 0;
+    }
+    if (IsLocal(target)) {
+        reply("OSMSG_FORCEKICK_LOCAL", target->nick);
+        return 0;
+    }
+    irc_kick(cmd->parent->bot, target, channel, reason);
+    return 1;
+}
+
 static MODCMD_FUNC(cmd_kickall)
 {
     unsigned int limit, n, inchan;
@@ -1129,7 +1159,7 @@ static MODCMD_FUNC(cmd_part)
         reply("OSMSG_NOT_ON_CHANNEL", cmd->parent->bot->nick, channel->name);
         return 0;
     }
-    reason = (argc < 3) ? "Leaving." : unsplit_string(argv+2, argc-2, NULL);
+    reason = (argc < 2) ? "Leaving." : unsplit_string(argv+1, argc-1, NULL);
     reply("OSMSG_LEAVING", channel->name);
     DelChannelUser(cmd->parent->bot, channel, reason, 0);
     return 1;
@@ -3084,6 +3114,12 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in
         } else if (irccasecmp(argv[i], "reason") == 0) {
             discrim->reason = strdup(unsplit_string(argv+i+1, argc-i-1, NULL));
             i = argc;
+        } else if (irccasecmp(argv[i], "notice_target") == 0 || irccasecmp(argv[i], "target") == 0) {
+            if (!IsChannelName(argv[i + 1])) {
+                send_message(user, opserv, "MSG_NOT_CHANNEL_NAME");
+                goto fail;
+            }
+            discrim->notice_target = argv[++i];
         } else if (irccasecmp(argv[i], "last") == 0) {
             discrim->min_ts = now - ParseInterval(argv[++i]);
         } else if ((irccasecmp(argv[i], "linked") == 0)
@@ -3311,7 +3347,7 @@ is_oper_victim(struct userNode *user, struct userNode *target, int match_opers,
                   || (target->handle_info
                       && target->handle_info->opserv_level > user->handle_info->opserv_level));
 
-    // If we don't need an ip check or want to hit opers or the the "cheap" check already disqualified the target, we are done.
+    /* If we don't need an ip check or want to hit opers or the the "cheap" check already disqualified the target, we are done. */
     if (!check_ip || match_opers || !is_victim)
         return is_victim;
 
@@ -3881,7 +3917,7 @@ alert_check_user(const char *key, void *data, void *extra)
         log_module(OS_LOG, LOG_ERROR, "Invalid reaction type %d for alert %s.", alert->reaction, key);
         /* fall through to REACT_NOTICE case */
     case REACT_NOTICE:
-        opserv_alert("Alert $b%s$b triggered by user $b%s$b!%s@%s (%s).", key, user->nick, user->ident, user->hostname, alert->discrim->reason);
+        opserv_custom_alert(alert->discrim->notice_target, "Alert $b%s$b triggered by user $b%s$b!%s@%s (%s).", key, user->nick, user->ident, user->hostname, alert->discrim->reason);
         break;
     }
     return 0;
@@ -4260,6 +4296,7 @@ init_opserv(const char *nick)
     opserv_define_func("JUMP", cmd_jump, 900, 0, 2);
     opserv_define_func("JUPE", cmd_jupe, 900, 0, 4);
     opserv_define_func("KICK", cmd_kick, 100, 2, 2);
+    opserv_define_func("FORCEKICK", cmd_forcekick, 800, 2, 2);
     opserv_define_func("KICKALL", cmd_kickall, 400, 2, 0);
     opserv_define_func("KICKBAN", cmd_kickban, 100, 2, 2);
     opserv_define_func("KICKBANALL", cmd_kickbanall, 450, 2, 0);