X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/182dd032e9ea762dcd8df317ba21cb9e9130fa88..0b565f3d116f2ac987e69ef517b16776835afbcf:/src/opserv.c diff --git a/src/opserv.c b/src/opserv.c index 6d7f3a8..011f8ce 100644 --- a/src/opserv.c +++ b/src/opserv.c @@ -101,6 +101,8 @@ static const struct message_entry msgtab[] = { { "OSMSG_NO_CHANNEL_MODES", "Channel $b%s$b had no modes to clear." }, { "OSMSG_DEOP_DONE", "Deopped the requested lusers." }, { "OSMSG_DEOPALL_DONE", "Deopped everyone on $b%s$b." }, + { "OSMSG_DEHOP_DONE", "Dehalfopped the requested lusers." }, + { "OSMSG_DEHOPALL_DONE", "Dehalfopped everyone on $b%s$b." }, { "OSMSG_NO_DEBUG_CHANNEL", "No debug channel has been configured." }, { "OSMSG_INVITE_DONE", "Invited $b%s$b to $b%s$b." }, { "OSMSG_ALREADY_THERE", "You are already in $b%s$b." }, @@ -112,6 +114,8 @@ static const struct message_entry msgtab[] = { { "OSMSG_MODE_SET", "I have set the modes for $b%s$b." }, { "OSMSG_OP_DONE", "Opped the requested lusers." }, { "OSMSG_OPALL_DONE", "Opped everyone on $b%s$b." }, + { "OSMSG_HOP_DONE", "Halfopped the requested lusers." }, + { "OSMSG_HOPALL_DONE", "Halfopped everyone on $b%s$b." }, { "OSMSG_WHOIS_IDENT", "%s (%s@%s) from %d.%d.%d.%d" }, { "OSMSG_WHOIS_NICK", "Nick : %s" }, { "OSMSG_WHOIS_HOST", "Host : %s@%s" }, @@ -177,6 +181,7 @@ static const struct message_entry msgtab[] = { { "OSMSG_CLONE_JOINED", "$b%s$b has joined $b%s$b." }, { "OSMSG_CLONE_PARTED", "$b%s$b has left $b%s$b." }, { "OSMSG_OPS_GIVEN", "I have given ops in $b%s$b to $b%s$b." }, + { "OSMSG_HOPS_GIVEN", "I have given halfops in $b%s$b to $b%s$b." }, { "OSMSG_CLONE_SAID", "$b%s$b has spoken to $b%s$b." }, { "OSMSG_UNKNOWN_SUBCOMMAND", "$b%s$b is not a valid subcommand of $b%s$b." }, { "OSMSG_UNKNOWN_OPTION", "$b%s$b has not been set." }, @@ -244,6 +249,8 @@ static const struct message_entry msgtab[] = { { "OSMSG_CHANINFO_TOPIC_UNKNOWN", "Topic: (none / not gathered)" }, { "OSMSG_CHANINFO_BAN_COUNT", "Bans (%d):" }, { "OSMSG_CHANINFO_BAN", "%%s by %%s (%a %b %d %H:%M:%S %Y)" }, + { "OSMSG_CHANINFO_EXEMPT_COUNT", "Exempts (%d):" }, + { "OSMSG_CHANINFO_EXEMPT", "%%s by %%s (%a %b %d %H:%M:%S %Y)" }, { "OSMSG_CHANINFO_MANY_USERS", "%d users (\"/msg $S %s %s users\" for the list)" }, { "OSMSG_CHANINFO_USER_COUNT", "Users (%d):" }, { "OSMSG_CSEARCH_CHANNEL_INFO", "%s [%d users] %s %s" }, @@ -402,16 +409,16 @@ static MODCMD_FUNC(cmd_ban) change.argc = 1; change.args[0].mode = MODE_BAN; if (is_ircmask(argv[1])) - change.args[0].hostmask = strdup(argv[1]); + change.args[0].u.hostmask = strdup(argv[1]); else if ((victim = GetUserH(argv[1]))) - change.args[0].hostmask = generate_hostmask(victim, 0); + change.args[0].u.hostmask = generate_hostmask(victim, 0); else { reply("OSMSG_INVALID_IRCMASK", argv[1]); return 0; } modcmd_chanmode_announce(&change); - reply("OSMSG_ADDED_BAN", change.args[0].hostmask, channel->name); - free((char*)change.args[0].hostmask); + reply("OSMSG_ADDED_BAN", change.args[0].u.hostmask, channel->name); + free((char*)change.args[0].u.hostmask); return 1; } @@ -420,6 +427,7 @@ static MODCMD_FUNC(cmd_chaninfo) char buffer[MAXLEN]; const char *fmt; struct banNode *ban; + struct exemptNode *exempt; struct modeNode *moden; unsigned int n; @@ -449,6 +457,15 @@ static MODCMD_FUNC(cmd_chaninfo) send_message_type(4, user, cmd->parent->bot, buffer, ban->ban, ban->who); } } + if (channel->exemptlist.used) { + reply("OSMSG_CHANINFO_EXEMPT_COUNT", channel->exemptlist.used); + fmt = user_find_message(user, "OSMSG_CHANINFO_EXEMPT"); + for (n = 0; n < channel->exemptlist.used; n++) { + exempt = channel->exemptlist.list[n]; + strftime(buffer, sizeof(buffer), fmt, localtime(&exempt->set)); + send_message_type(4, user, cmd->parent->bot, buffer, exempt->exempt, exempt->who); + } + } if ((argc < 2) && (channel->members.used >= 50)) { /* early out unless they ask for users */ reply("OSMSG_CHANINFO_MANY_USERS", channel->members.used, argv[0], channel->name); @@ -460,14 +477,19 @@ static MODCMD_FUNC(cmd_chaninfo) if (moden->modes & MODE_CHANOP) send_message_type(4, user, cmd->parent->bot, " @%s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname); } + for (n=0; nmembers.used; n++) { + moden = channel->members.list[n]; + if ((moden->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE)) == MODE_HALFOP) + send_message_type(4, user, cmd->parent->bot, " %s%s (%s@%s)", "%", moden->user->nick, moden->user->ident, moden->user->hostname); + } for (n=0; nmembers.used; n++) { moden = channel->members.list[n]; - if ((moden->modes & (MODE_CHANOP|MODE_VOICE)) == MODE_VOICE) + if ((moden->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE)) == MODE_VOICE) send_message_type(4, user, cmd->parent->bot, " +%s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname); } for (n=0; nmembers.used; n++) { moden = channel->members.list[n]; - if ((moden->modes & (MODE_CHANOP|MODE_VOICE)) == 0) + if ((moden->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE)) == 0) send_message_type(4, user, cmd->parent->bot, " %s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname); } return 1; @@ -522,7 +544,7 @@ static MODCMD_FUNC(cmd_clearbans) change = mod_chanmode_alloc(channel->banlist.used); for (ii=0; iibanlist.used; ii++) { change->args[ii].mode = MODE_REMOVE | MODE_BAN; - change->args[ii].hostmask = channel->banlist.list[ii]->ban; + change->args[ii].u.hostmask = channel->banlist.list[ii]->ban; } modcmd_chanmode_announce(change); mod_chanmode_free(change); @@ -559,7 +581,7 @@ static MODCMD_FUNC(cmd_deop) || !(mn->modes & MODE_CHANOP)) continue; change->args[count].mode = MODE_REMOVE | MODE_CHANOP; - change->args[count++].member = mn; + change->args[count++].u.member = mn; } if (count) { change->argc = count; @@ -570,6 +592,31 @@ static MODCMD_FUNC(cmd_deop) return 1; } +static MODCMD_FUNC(cmd_dehop) +{ + struct mod_chanmode *change; + unsigned int arg, count; + + change = mod_chanmode_alloc(argc-1); + for (arg = 1, count = 0; arg < argc; ++arg) { + struct userNode *victim = GetUserH(argv[arg]); + struct modeNode *mn; + if (!victim || IsService(victim) + || !(mn = GetUserMode(channel, victim)) + || !(mn->modes & MODE_HALFOP)) + continue; + change->args[count].mode = MODE_REMOVE | MODE_HALFOP; + change->args[count++].u.member = mn; + } + if (count) { + change->argc = count; + modcmd_chanmode_announce(change); + } + mod_chanmode_free(change); + reply("OSMSG_DEHOP_DONE"); + return 1; +} + static MODCMD_FUNC(cmd_deopall) { struct mod_chanmode *change; @@ -581,7 +628,7 @@ static MODCMD_FUNC(cmd_deopall) if (IsService(mn->user) || !(mn->modes & MODE_CHANOP)) continue; change->args[count].mode = MODE_REMOVE | MODE_CHANOP; - change->args[count++].member = mn; + change->args[count++].u.member = mn; } if (count) { change->argc = count; @@ -592,6 +639,28 @@ static MODCMD_FUNC(cmd_deopall) return 1; } +static MODCMD_FUNC(cmd_dehopall) +{ + struct mod_chanmode *change; + unsigned int ii, count; + + change = mod_chanmode_alloc(channel->members.used); + for (ii = count = 0; ii < channel->members.used; ++ii) { + struct modeNode *mn = channel->members.list[ii]; + if (IsService(mn->user) || !(mn->modes & MODE_HALFOP)) + continue; + change->args[count].mode = MODE_REMOVE | MODE_HALFOP; + change->args[count++].u.member = mn; + } + if (count) { + change->argc = count; + modcmd_chanmode_announce(change); + } + mod_chanmode_free(change); + reply("OSMSG_DEHOPALL_DONE", channel->name); + return 1; +} + static MODCMD_FUNC(cmd_rehash) { extern char *services_config; @@ -856,6 +925,8 @@ opserv_ison(struct userNode *tell, struct userNode *target, const char *message) } if (mn->modes & MODE_CHANOP) buff[count++] = '@'; + if (mn->modes & MODE_HALFOP) + buff[count++] = '%'; if (mn->modes & MODE_VOICE) buff[count++] = '+'; memcpy(buff+count, mn->channel->name, here_len); @@ -886,7 +957,7 @@ static MODCMD_FUNC(cmd_inviteme) return 0; } if (GetUserMode(opserv_conf.debug_channel, user)) { - reply("OSMSG_ALREADY_THERE", channel->name); + reply("OSMSG_ALREADY_THERE", opserv_conf.debug_channel->name); return 0; } irc_invite(cmd->parent->bot, target, opserv_conf.debug_channel); @@ -913,7 +984,7 @@ static MODCMD_FUNC(cmd_join) reply("MSG_NOT_CHANNEL_NAME"); return 0; } else if (!(channel = GetChannel(argv[1]))) { - channel = AddChannel(argv[1], now, NULL, NULL); + channel = AddChannel(argv[1], now, NULL, NULL, NULL); AddChannelUser(bot, channel)->modes |= MODE_CHANOP; } else if (GetUserMode(channel, bot)) { reply("OSMSG_ALREADY_JOINED", channel->name); @@ -923,7 +994,7 @@ static MODCMD_FUNC(cmd_join) mod_chanmode_init(&change); change.argc = 1; change.args[0].mode = MODE_CHANOP; - change.args[0].member = AddChannelUser(bot, channel); + change.args[0].u.member = AddChannelUser(bot, channel); modcmd_chanmode_announce(&change); } irc_fetchtopic(bot, channel->name); @@ -968,7 +1039,7 @@ static MODCMD_FUNC(cmd_kickall) struct mod_chanmode change; mod_chanmode_init(&change); change.args[0].mode = MODE_CHANOP; - change.args[0].member = AddChannelUser(bot, channel); + change.args[0].u.member = AddChannelUser(bot, channel); modcmd_chanmode_announce(&change); } if (argc < 2) { @@ -1018,7 +1089,7 @@ static MODCMD_FUNC(cmd_kickban) mod_chanmode_init(&change); change.argc = 1; change.args[0].mode = MODE_BAN; - change.args[0].hostmask = mask = generate_hostmask(target, 0); + change.args[0].u.hostmask = mask = generate_hostmask(target, 0); modcmd_chanmode_announce(&change); KickChannelUser(target, channel, cmd->parent->bot, reason); free(mask); @@ -1038,15 +1109,16 @@ static MODCMD_FUNC(cmd_kickbanall) if (!(inchan = GetUserMode(channel, bot) ? 1 : 0)) { change = mod_chanmode_alloc(2); change->args[0].mode = MODE_CHANOP; - change->args[0].member = AddChannelUser(bot, channel); + change->args[0].u.member = AddChannelUser(bot, channel); change->args[1].mode = MODE_BAN; - change->args[1].hostmask = "*!*@*"; + change->args[1].u.hostmask = "*!*@*"; } else { change = mod_chanmode_alloc(1); change->args[0].mode = MODE_BAN; - change->args[0].hostmask = "*!*@*"; + change->args[0].u.hostmask = "*!*@*"; } modcmd_chanmode_announce(change); + mod_chanmode_free(change); if (argc < 2) { reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1); sprintf(reason, OSMSG_KICK_REQUESTED, user->nick); @@ -1116,7 +1188,7 @@ static MODCMD_FUNC(cmd_op) if (mn->modes & MODE_CHANOP) continue; change->args[count].mode = MODE_CHANOP; - change->args[count++].member = mn; + change->args[count++].u.member = mn; } if (count) { change->argc = count; @@ -1127,6 +1199,33 @@ static MODCMD_FUNC(cmd_op) return 1; } +static MODCMD_FUNC(cmd_hop) +{ + struct mod_chanmode *change; + unsigned int arg, count; + + change = mod_chanmode_alloc(argc-1); + for (arg = 1, count = 0; arg < argc; ++arg) { + struct userNode *victim; + struct modeNode *mn; + if (!(victim = GetUserH(argv[arg]))) + continue; + if (!(mn = GetUserMode(channel, victim))) + continue; + if (mn->modes & MODE_HALFOP) + continue; + change->args[count].mode = MODE_HALFOP; + change->args[count++].u.member = mn; + } + if (count) { + change->argc = count; + modcmd_chanmode_announce(change); + } + mod_chanmode_free(change); + reply("OSMSG_HOP_DONE"); + return 1; +} + static MODCMD_FUNC(cmd_opall) { struct mod_chanmode *change; @@ -1138,7 +1237,7 @@ static MODCMD_FUNC(cmd_opall) if (mn->modes & MODE_CHANOP) continue; change->args[count].mode = MODE_CHANOP; - change->args[count++].member = mn; + change->args[count++].u.member = mn; } if (count) { change->argc = count; @@ -1149,6 +1248,28 @@ static MODCMD_FUNC(cmd_opall) return 1; } +static MODCMD_FUNC(cmd_hopall) +{ + struct mod_chanmode *change; + unsigned int ii, count; + + change = mod_chanmode_alloc(channel->members.used); + for (ii = count = 0; ii < channel->members.used; ++ii) { + struct modeNode *mn = channel->members.list[ii]; + if (mn->modes & MODE_HALFOP) + continue; + change->args[count].mode = MODE_HALFOP; + change->args[count++].u.member = mn; + } + if (count) { + change->argc = count; + modcmd_chanmode_announce(change); + } + mod_chanmode_free(change); + reply("OSMSG_HOPALL_DONE", channel->name); + return 1; +} + static MODCMD_FUNC(cmd_whois) { struct userNode *target; @@ -1159,10 +1280,8 @@ static MODCMD_FUNC(cmd_whois) if (argv[1][0] == '*') target = GetUserN(argv[1]+1); else - target = GetUserH(argv[1]); -#else - target = GetUserH(argv[1]); #endif + target = GetUserH(argv[1]); if (!target) { reply("MSG_NICK_UNKNOWN", argv[1]); return 0; @@ -1215,7 +1334,7 @@ static MODCMD_FUNC(cmd_unban) mod_chanmode_init(&change); change.argc = 1; change.args[0].mode = MODE_REMOVE | MODE_BAN; - change.args[0].hostmask = argv[1]; + change.args[0].u.hostmask = argv[1]; modcmd_chanmode_announce(&change); reply("OSMSG_UNBAN_DONE", channel->name); return 1; @@ -1229,10 +1348,10 @@ static MODCMD_FUNC(cmd_voiceall) change = mod_chanmode_alloc(channel->members.used); for (ii = count = 0; ii < channel->members.used; ++ii) { struct modeNode *mn = channel->members.list[ii]; - if (mn->modes & (MODE_CHANOP|MODE_VOICE)) + if (mn->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE)) continue; change->args[count].mode = MODE_VOICE; - change->args[count++].member = mn; + change->args[count++].u.member = mn; } if (count) { change->argc = count; @@ -1254,7 +1373,7 @@ static MODCMD_FUNC(cmd_devoiceall) if (!(mn->modes & MODE_VOICE)) continue; change->args[count].mode = MODE_REMOVE | MODE_VOICE; - change->args[count++].member = mn; + change->args[count++].u.member = mn; } if (count) { change->argc = count; @@ -1806,9 +1925,9 @@ opserv_shutdown_channel(struct chanNode *channel, const char *reason) change = mod_chanmode_alloc(2); change->modes_set = MODE_SECRET | MODE_INVITEONLY; change->args[0].mode = MODE_CHANOP; - change->args[0].member = AddChannelUser(opserv, channel); + change->args[0].u.member = AddChannelUser(opserv, channel); change->args[1].mode = MODE_BAN; - change->args[1].hostmask = "*!*@*"; + change->args[1].u.hostmask = "*!*@*"; mod_chanmode_announce(opserv, channel, change); mod_chanmode_free(change); for (nn=channel->members.used; nn>0; ) { @@ -1887,7 +2006,7 @@ opserv_join_check(struct modeNode *mNode) if (!GetUserMode(channel, opserv)) { /* If we aren't in the channel, join it. */ change.args[0].mode = MODE_CHANOP; - change.args[0].member = AddChannelUser(opserv, channel); + change.args[0].u.member = AddChannelUser(opserv, channel); change.argc++; } if (!(channel->modes & MODE_MODERATED)) @@ -2259,7 +2378,7 @@ static MODCMD_FUNC(cmd_clone) channel = GetChannel(argv[3]); if (!irccasecmp(argv[1], "JOIN")) { if (!channel - && !(channel = AddChannel(argv[3], now, NULL, NULL))) { + && !(channel = AddChannel(argv[3], now, NULL, NULL, NULL))) { reply("MSG_CHANNEL_UNKNOWN", argv[3]); return 0; } @@ -2289,8 +2408,8 @@ static MODCMD_FUNC(cmd_clone) mod_chanmode_init(&change); change.argc = 1; change.args[0].mode = MODE_CHANOP; - change.args[0].member = GetUserMode(channel, clone); - if (!change.args[0].member) { + change.args[0].u.member = GetUserMode(channel, clone); + if (!change.args[0].u.member) { reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name); return 0; } @@ -2298,6 +2417,24 @@ static MODCMD_FUNC(cmd_clone) reply("OSMSG_OPS_GIVEN", channel->name, clone->nick); return 1; } + if (!irccasecmp(argv[1], "HOP")) { + struct mod_chanmode change; + if (!channel) { + reply("MSG_CHANNEL_UNKNOWN", argv[3]); + return 0; + } + mod_chanmode_init(&change); + change.argc = 1; + change.args[0].mode = MODE_HALFOP; + change.args[0].u.member = GetUserMode(channel, clone); + if (!change.args[0].u.member) { + reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name); + return 0; + } + modcmd_chanmode_announce(&change); + reply("OSMSG_HOPS_GIVEN", channel->name, clone->nick); + return 1; + } if (argc < 5) { reply("MSG_MISSING_PARAMS", argv[1]); OPSERV_SYNTAX(); @@ -2958,11 +3095,17 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in case '#': goto find_channel; case '-': - discrim->chan_no_modes |= MODE_CHANOP | MODE_VOICE; + discrim->chan_no_modes |= MODE_CHANOP | MODE_HALFOP | MODE_VOICE; break; case '+': discrim->chan_req_modes |= MODE_VOICE; discrim->chan_no_modes |= MODE_CHANOP; + discrim->chan_no_modes |= MODE_HALFOP; + break; + case '%': + discrim->chan_req_modes |= MODE_HALFOP; + discrim->chan_no_modes |= MODE_CHANOP; + discrim->chan_no_modes |= MODE_VOICE; break; case '@': discrim->chan_req_modes |= MODE_CHANOP; @@ -2981,7 +3124,7 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in send_message(user, opserv, "MSG_CHANNEL_UNKNOWN", argv[i]); goto fail; } else { - discrim->channel = AddChannel(argv[i]+j, now, NULL, NULL); + discrim->channel = AddChannel(argv[i]+j, now, NULL, NULL, NULL); } } LockChannel(discrim->channel); @@ -3907,7 +4050,7 @@ opserv_conf_read(void) str2 = database_get_data(conf_node, KEY_DEBUG_CHANNEL_MODES, RECDB_QSTRING); if (!str2) str2 = "+tinms"; - opserv_conf.debug_channel = AddChannel(str, now, str2, NULL); + opserv_conf.debug_channel = AddChannel(str, now, str2, NULL, NULL); AddChannelUser(opserv, opserv_conf.debug_channel)->modes |= MODE_CHANOP; } else { opserv_conf.debug_channel = NULL; @@ -3917,7 +4060,7 @@ opserv_conf_read(void) str2 = database_get_data(conf_node, KEY_ALERT_CHANNEL_MODES, RECDB_QSTRING); if (!str2) str2 = "+tns"; - opserv_conf.alert_channel = AddChannel(str, now, str2, NULL); + opserv_conf.alert_channel = AddChannel(str, now, str2, NULL, NULL); AddChannelUser(opserv, opserv_conf.alert_channel)->modes |= MODE_CHANOP; } else { opserv_conf.alert_channel = NULL; @@ -3927,7 +4070,7 @@ opserv_conf_read(void) str2 = database_get_data(conf_node, KEY_STAFF_AUTH_CHANNEL_MODES, RECDB_QSTRING); if (!str2) str2 = "+timns"; - opserv_conf.staff_auth_channel = AddChannel(str, now, str2, NULL); + opserv_conf.staff_auth_channel = AddChannel(str, now, str2, NULL, NULL); AddChannelUser(opserv, opserv_conf.staff_auth_channel)->modes |= MODE_CHANOP; } else { opserv_conf.staff_auth_channel = NULL; @@ -4026,8 +4169,10 @@ void init_opserv(const char *nick) { OS_LOG = log_register_type("OpServ", "file:opserv.log"); - if (nick) - opserv = AddService(nick, "Oper Services", NULL); + if (nick) { + const char *modes = conf_get_data("services/opserv/modes", RECDB_QSTRING); + opserv = AddService(nick, modes ? modes : NULL, "Oper Services", NULL); + } conf_register_reload(opserv_conf_read); memset(level_strings, 0, sizeof(level_strings)); @@ -4056,6 +4201,8 @@ init_opserv(const char *nick) opserv_define_func("DELTRUST", cmd_deltrust, 800, 0, 2); opserv_define_func("DEOP", cmd_deop, 100, 2, 2); opserv_define_func("DEOPALL", cmd_deopall, 400, 2, 0); + opserv_define_func("DEHOP", cmd_dehop, 100, 2, 2); + opserv_define_func("DEHOPALL", cmd_dehopall, 400, 2, 0); opserv_define_func("DEVOICEALL", cmd_devoiceall, 300, 2, 0); opserv_define_func("DIE", cmd_die, 900, 0, 2); opserv_define_func("DUMP", cmd_dump, 999, 0, 2); @@ -4079,6 +4226,8 @@ init_opserv(const char *nick) opserv_define_func("MODE", cmd_mode, 100, 2, 2); opserv_define_func("OP", cmd_op, 100, 2, 2); opserv_define_func("OPALL", cmd_opall, 400, 2, 0); + opserv_define_func("HOP", cmd_hop, 100, 2, 2); + opserv_define_func("HOPALL", cmd_hopall, 400, 2, 0); opserv_define_func("PART", cmd_part, 601, 0, 2); opserv_define_func("QUERY", cmd_query, 0, 0, 0); opserv_define_func("RAW", cmd_raw, 999, 0, 2);