{"MODCMD_PRIVILEGED", "$b%s$b is a privileged command."}, /* {ARGS: "god"} */
{"MODCMD_PUBCMD", "Public commands in $b%s$b are restricted."}, /* {ARGS: "#TestChan"} */
{"MODCMD_ACCESS_DENIED", "Access denied."},
+ {"MODCMD_SUBCOMMANDS", "Subcommands of %s: %s"}, /* {ARGS: "bot", "ADD, DEL, EDIT"} */
+ {"MODCMD_CROSSCHAN", "You must be in %s (or on its userlist) to use this command."},
{NULL, NULL}
};
return 26;
}
-struct ClientSocket* get_prefered_bot(int botid) {
+struct ClientSocket* get_botwise_prefered_bot(int botid, int clientid) {
struct ClientSocket *client, *lowbot = NULL;
for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
- if(client->botid == botid) {
+ if(client->botid == botid && (botid || clientid == client->clientid)) {
if((client->flags & SOCKET_FLAG_PREFERRED))
return client;
else
return lowbot;
}
-static char* get_channel_trigger(int botid, struct ChanNode *chan) {
+static char* get_channel_trigger(int botid, int clientid, struct ChanNode *chan) {
struct trigger_cache *trigger;
for(trigger = chan->trigger; trigger; trigger = trigger->next) {
- if(trigger->botid == botid)
+ if(trigger->botid == botid && (botid || trigger->clientid == clientid))
return trigger->trigger;
}
struct trigger_callback *cb;
}
char triggerStr[TRIGGERLEN];
if(cb)
- cb->func(chan, triggerStr);
+ cb->func(clientid, chan, triggerStr);
else
- strcpy(triggerStr, "+");
+ triggerStr[0] = '\0';
trigger = malloc(sizeof(*trigger));
if (!trigger) {
perror("malloc() failed");
return 0;
}
trigger->botid = botid;
+ trigger->clientid = clientid;
trigger->trigger = (triggerStr[0] ? strdup(triggerStr) : NULL);
trigger->next = chan->trigger;
chan->trigger = trigger;
free(cache);
}
+static CMD_BIND(modcmd_linker) {
+ //fake command for subcommands
+ //just empty
+}
+
static void handle_command(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, char *message) {
struct ChanNode *sent_chan = chan;
if(message[0] == '#') {
}
struct cmd_binding *cbind;
for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
- if(cbind->botid == client->botid && stricmp(cbind->cmd, message) == 0) {
+ if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && stricmp(cbind->cmd, message) == 0) {
+ if(cbind->func->func == modcmd_linker) {
+ //links subcommands
+ char command[MAXLEN];
+ struct cmd_binding *parent_bind = cbind;
+ if(args) {
+ char *subcmd = args;
+ args = strstr(args, " ");
+ if(args) {
+ *args = '\0';
+ args++;
+ }
+ sprintf(command, "%s %s", parent_bind->cmd, subcmd);
+ for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
+ if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && stricmp(cbind->cmd, command) == 0)
+ break;
+ }
+ if(!cbind)
+ break;
+ } else {
+ //list all sub commands
+ int commandlen = sprintf(command, "%s ", parent_bind->cmd);
+ char subcommands[MAXLEN];
+ int subcompos = 0;
+ for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
+ if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && stricmplen(cbind->cmd, command, commandlen) == 0) {
+ subcompos += sprintf(subcommands + subcompos, (subcompos ? ", %s" : "%s"), cbind->cmd + commandlen);
+ }
+ }
+ reply(tmp_text_client, user, "MODCMD_SUBCOMMANDS", parent_bind->cmd, subcommands);
+ break;
+ }
+ }
if(statistics_enabled)
statistics_commands++;
total_triggered++;
- cbind->func->triggered++;
+ cbind->triggered++;
if((cbind->func->flags & CMDFLAG_FUNCMD)) {
if(!sent_chan)
break;
chan = sent_chan;
}
//get a text bot
- tmp_text_client = get_prefered_bot(client->botid);
+ tmp_text_client = get_botwise_prefered_bot(client->botid, (client->botid == 0 ? client->clientid : 0));
//parse the arguments...
char *arga[MAXNUMPARAMS];
char **argv;
int argc = 0;
+ int escape = 0;
+ int offset = 0;
if(args) {
while(*args) {
//skip leading spaces
arga[argc++] = args;
if (argc >= MAXNUMPARAMS)
break;
- while (*args != ' ' && *args)
+ while ((escape || *args != ' ') && *args) {
+ if((cbind->func->flags & CMDFLAG_ESCAPE_ARGS) && *args == '\\') {
+ escape = 1;
+ offset++;
+ } else if(escape)
+ escape = 0;
+ if(!escape && offset) {
+ args[0 - offset] = args[0];
+ }
args++;
+
+ }
+ if(offset) {
+ args[0-offset] = '\0';
+ offset = 0;
+ }
}
}
argv = arga;
int args_pos = 0;
char *uargs[MAXNUMPARAMS];
int uargc = 0;
- char *b;
+ char *b, *c;
int i;
int allargs, argi;
for(i = 0; i < cbind->paramcount; i++) {
b = cbind->parameters[i];
if(b[0] == '%') {
b++;
+ c = b;
+ while(*c && *c != ' ') {
+ if(*c == '|') break;
+ c++;
+ }
+ if(!*c || *c == ' ') c = NULL;
+ else {
+ *c = '\0';
+ c++;
+ }
if(b[strlen(b)-1] == '-') {
allargs = strlen(b)-1;
b[allargs] = '\0';
} else if((cbind->func->flags & CMDFLAG_EMPTY_ARGS)) {
uargs[uargc++] = args_buffer + args_pos;
args_buffer[args_pos++] = '\0';
+ } else if(c) {
+ uargs[uargc++] = args_buffer + args_pos;
+ args_pos += sprintf(args_buffer + args_pos, "%s", c) + 1;
}
} else if(!strcmp(b, "c")) {
uargs[uargc++] = args_buffer + args_pos;
uargs[uargc++] = args_buffer + args_pos;
args_pos += sprintf(args_buffer + args_pos, "%s", user->nick) + 1;
}
+ if(c) c[-1] = '|'; //reset \0 to |
} else {
uargs[uargc++] = args_buffer + args_pos;
args_pos += sprintf(args_buffer + args_pos, "%s", b) + 1;
reply(tmp_text_client, user, "MODCMD_CHAN_REQUIRED");
break;
}
- if((cbind->func->flags & CMDFLAG_CHECK_AUTH) && !(user->flags & USERFLAG_ISAUTHED)) {
+ if(((cbind->func->flags & CMDFLAG_CHECK_AUTH) || (chan && chan != sent_chan && !isUserOnChan(user, chan))) && !(user->flags & USERFLAG_ISAUTHED)) {
//check auth...
struct command_check_user_cache *data = malloc(sizeof(*data));
char **temp_argv = malloc(argc*sizeof(*temp_argv));
MYSQL_RES *res;
MYSQL_ROW row;
int uaccess;
+ char requested_uaccess = 0;
int eventflags = (cbind->func->flags & (CMDFLAG_LOG | CMDFLAG_OPLOG));
if((cbind->func->flags & CMDFLAG_REQUIRE_AUTH) && !(user->flags & USERFLAG_ISAUTHED)) {
reply(tmp_text_client, user, "MODCMD_AUTH_REQUIRED");
return;
}
+ if(chan && sent_chan != chan && !isUserOnChan(user, chan)) {
+ char user_in_chan = 0;
+ if((user->flags & USERFLAG_ISAUTHED)) {
+ //maybe there's another user authed to user->auth on the channel...
+ struct ChanUser *cchanuser;
+ for(cchanuser = getChannelUsers(chan, NULL); cchanuser; cchanuser = getChannelUsers(chan, cchanuser)) {
+ if((cchanuser->user->flags & USERFLAG_ISAUTHED) && !stricmp(user->auth, cchanuser->user->auth)) {
+ user_in_chan = 1;
+ break;
+ }
+ }
+ }
+ if(!user_in_chan) {
+ //check if we are allowed to execute commands in this channel
+ requested_uaccess = 1;
+ uaccess = getChannelAccess(user, chan);
+ if(!uaccess) {
+ reply(tmp_text_client, user, "MODCMD_CROSSCHAN", chan->name);
+ return;
+ }
+ }
+ }
if(sent_chan && sent_chan != chan) {
//check pubcmd of this channel
printf_mysql_query("SELECT `channel_pubcmd` FROM `channels` WHERE `channel_name` = '%s'", escape_string(sent_chan->name));
res = mysql_use();
if ((row = mysql_fetch_row(res)) != NULL) {
- uaccess = getChannelAccess(user, sent_chan);
- if(row[0] && uaccess < atoi(row[0]) && !isGodMode(user)) { //NOTE: HARDCODED DEFAULT: pubcmd = 0
+ int saccess = getChannelAccess(user, sent_chan);
+ if(row[0] && saccess < atoi(row[0]) && !isGodMode(user)) { //NOTE: HARDCODED DEFAULT: pubcmd = 0
reply(tmp_text_client, user, "MODCMD_PUBCMD", sent_chan->name);
return;
}
chan->flags |= CHANFLAG_CHAN_REGISTERED;
chan->channel_id = atoi(row[0]);
if((sent_chan && sent_chan == chan) || access_count || minaccess) {
- uaccess = getChannelAccess(user, chan);
+ if(!requested_uaccess) uaccess = getChannelAccess(user, chan);
if(uaccess < minaccess && isGodMode(user)) {
eventflags |= CMDFLAG_OPLOG;
} else if(uaccess < minaccess) {
reply(tmp_text_client, user, "MODCMD_PRIVILEGED", cbind->cmd);
return;
}
- struct Event *event = createEvent(client, user, chan, cbind->func->name, argv, argc, eventflags);
+ struct Event *event = createEvent(client, user, chan, cbind, argv, argc, eventflags);
cbind->func->func(client, user, chan, argv, argc, event);
}
static void got_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message) {
- fd_set fds;
+ fd_set fds, fds2;
char *trigger;
struct ClientSocket *client;
FD_ZERO(&fds);
+ FD_ZERO(&fds2);
for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
- if(isUserOnChan(client->user, chan) && (client->flags & SOCKET_FLAG_PREFERRED) && !FD_ISSET(client->botid, &fds)) {
- FD_SET(client->botid, &fds);
- trigger = get_channel_trigger(client->botid, chan);
+ if(isUserOnChan(client->user, chan) && (client->flags & SOCKET_FLAG_PREFERRED) && ((client->botid == 0 && !FD_ISSET(client->clientid, &fds)) || (client->botid && !FD_ISSET(client->botid, &fds2)))) {
+ FD_SET(client->clientid, &fds);
+ FD_SET(client->botid, &fds2);
+ trigger = get_channel_trigger(client->botid, client->clientid, chan);
if(trigger && stricmplen(message, trigger, strlen(trigger)) == 0) {
handle_command(client, user, chan, message + strlen(trigger));
}
}
}
for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) {
- if(isUserOnChan(client->user, chan) && !FD_ISSET(client->botid, &fds)) {
+ if(isUserOnChan(client->user, chan) && ((client->botid == 0 && !FD_ISSET(client->clientid, &fds)) || (client->botid && !FD_ISSET(client->botid, &fds2)))) {
FD_SET(client->botid, &fds);
- trigger = get_channel_trigger(client->botid, chan);
+ FD_SET(client->botid, &fds2);
+ trigger = get_channel_trigger(client->botid, client->clientid, chan);
if(trigger && stricmplen(message, trigger, strlen(trigger)) == 0) {
handle_command(client, user, chan, message + strlen(trigger));
}
cmdfunc->paramcount = paramcount;
cmdfunc->channel_access = channel_access;
cmdfunc->global_access = global_access;
- cmdfunc->triggered = 0;
cmdfunc->next = cmd_functions;
cmd_functions = cmdfunc;
return 1;
return 1;
}
-int changeChannelTrigger(int botid, struct ChanNode *chan, char *new_trigger) {
+int changeBotwiseChannelTrigger(int botid, int clientid, struct ChanNode *chan, char *new_trigger) {
struct trigger_cache *trigger;
for(trigger = chan->trigger; trigger; trigger = trigger->next) {
- if(trigger->botid == botid) {
+ if(trigger->botid == botid && (botid || trigger->clientid == clientid)) {
free(trigger->trigger);
trigger->trigger = strdup(new_trigger);
return 1;
return 0;
}
-int bind_cmd_to_function(int botid, char *cmd, struct cmd_function *func) {
+int bind_botwise_cmd_to_function(int botid, int clientid, char *cmd, struct cmd_function *func) {
int bind_index = get_binds_index(cmd[0]);
struct cmd_binding *cbind;
for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
- if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0)
+ if(((botid && cbind->botid == botid) || (botid == 0 && clientid == cbind->clientid)) && strcmp(cbind->cmd, cmd) == 0)
return 0;
}
cbind = malloc(sizeof(*cbind));
return 0;
}
cbind->botid = botid;
+ cbind->clientid = clientid;
cbind->cmd = strdup(cmd);
cbind->func = func;
cbind->paramcount = 0;
cbind->global_access = 0;
cbind->channel_access = NULL;
cbind->flags = 0;
+ cbind->triggered = 0;
cbind->next = cmd_binds[bind_index];
cmd_binds[bind_index] = cbind;
return 1;
}
-int bind_cmd_to_command(int botid, char *cmd, char *func) {
+int bind_botwise_cmd_to_command(int botid, int clientid, char *cmd, char *func) {
struct cmd_function *cmdfunc;
int fbotid = botid;
char *c;
int bind_index = get_binds_index(cmd[0]);
struct cmd_binding *cbind;
for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
- if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0)
+ if(((botid && cbind->botid == botid) || (botid == 0 && clientid == cbind->clientid)) && strcmp(cbind->cmd, cmd) == 0)
return 0;
}
cbind = malloc(sizeof(*cbind));
return 0;
}
cbind->botid = botid;
+ cbind->clientid = clientid;
cbind->cmd = strdup(cmd);
cbind->func = cmdfunc;
cbind->next = cmd_binds[bind_index];
cbind->global_access = 0;
cbind->channel_access = NULL;
cbind->flags = 0;
+ cbind->triggered = 0;
cmd_binds[bind_index] = cbind;
return 1;
}
-int unbind_cmd(int botid, char *cmd) {
+int unbind_botwise_cmd(int botid, int clientid, char *cmd) {
int bind_index = get_binds_index(cmd[0]);
struct cmd_binding *cbind, *last = NULL;
for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
- if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0) {
+ if(cbind->botid == botid && (botid || clientid == cbind->clientid) && strcmp(cbind->cmd, cmd) == 0) {
if(last)
last->next = cbind->next;
else
for(i = 0; i < cbind->paramcount; i++)
free(cbind->parameters[i]);
}
+ if(cbind->channel_access)
+ free(cbind->channel_access);
free(cbind);
return 1;
} else
return 0;
}
+int unbind_botwise_allcmd(int clientid) {
+ int i;
+ for(i = 0; i < 27; i++) {
+ struct cmd_binding *cbind, *next, *last = NULL;
+ for(cbind = cmd_binds[i]; cbind; cbind = next) {
+ next = cbind->next;
+ if(clientid == cbind->clientid) {
+ if(last)
+ last->next = cbind->next;
+ else
+ cmd_binds[i] = cbind->next;
+ free(cbind->cmd);
+ if(cbind->paramcount) {
+ int j;
+ for(j = 0; j < cbind->paramcount; j++)
+ free(cbind->parameters[j]);
+ }
+ if(cbind->channel_access)
+ free(cbind->channel_access);
+ free(cbind);
+ } else
+ last = cbind;
+ }
+ }
+ return 1;
+}
+
struct cmd_function *find_cmd_function(int botid, char *name) {
struct cmd_function *cmdfunc;
char *c;
bind_chanmsg(got_chanmsg);
bind_privmsg(got_privmsg);
register_default_language_table(msgtab);
+ register_command(0, "linker", modcmd_linker, 0, 0, 0, 0); //fake command for subcommands
}
void free_modcmd() {
bot_aliases = NULL;
}
-void bind_set_parameters(int botid, char *cmd, char *parameters) {
+void bind_botwise_set_parameters(int botid, int clientid, char *cmd, char *parameters) {
int bind_index = get_binds_index(cmd[0]);
struct cmd_binding *cbind;
for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
- if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0) {
+ if(cbind->botid == botid && (botid || clientid == cbind->clientid) && strcmp(cbind->cmd, cmd) == 0) {
if(cbind->paramcount) {
int i;
for(i = 0; i < cbind->paramcount; i++)
}
}
-void bind_set_global_access(int botid, char *cmd, int gaccess) {
+void bind_botwise_set_global_access(int botid, int clientid, char *cmd, int gaccess) {
int bind_index = get_binds_index(cmd[0]);
struct cmd_binding *cbind;
for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
- if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0) {
+ if(cbind->botid == botid && (botid || clientid == cbind->clientid) && strcmp(cbind->cmd, cmd) == 0) {
if(gaccess > -1) {
cbind->global_access = gaccess;
cbind->flags |= CMDFLAG_OVERRIDE_GLOBAL_ACCESS;
}
}
-void bind_set_channel_access(int botid, char *cmd, char *chanaccess) {
+void bind_botwise_set_channel_access(int botid, int clientid, char *cmd, char *chanaccess) {
int bind_index = get_binds_index(cmd[0]);
struct cmd_binding *cbind;
for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
- if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0) {
+ if(cbind->botid == botid && (botid || clientid == cbind->clientid) && strcmp(cbind->cmd, cmd) == 0) {
if(cbind->channel_access)
free(cbind->channel_access);
if(chanaccess) {
}
}
-struct cmd_binding *find_cmd_binding(int botid, char *cmd) {
+struct cmd_binding *find_botwise_cmd_binding(int botid, int clientid, char *cmd) {
int bind_index = get_binds_index(cmd[0]);
struct cmd_binding *cbind;
for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) {
- if(cbind->botid == botid && strcmp(cbind->cmd, cmd) == 0) {
+ if(cbind->botid == botid && (botid || clientid == cbind->clientid) && strcmp(cbind->cmd, cmd) == 0) {
return cbind;
}
}
return NULL;
}
-void bind_unbound_required_functions(int botid) {
+void bind_botwise_unbound_required_functions(int botid, int clientid) {
struct cmd_function *cmdfunc;
int i, found;
struct cmd_binding *cbind;
found = 0;
for(i = 0; i < 27; i++) {
for(cbind = cmd_binds[i]; cbind; cbind = cbind->next) {
- if(cbind->botid == botid && cbind->func == cmdfunc) {
+ if(cbind->botid == botid && (botid || clientid == cbind->clientid) && cbind->func == cmdfunc) {
found = 1;
break;
}
if(found)
break;
}
- if(!found && bind_cmd_to_function(botid, cmdfunc->name, cmdfunc)) {
- cbind = find_cmd_binding(botid, cmdfunc->name);
+ if(!found && bind_botwise_cmd_to_function(botid, clientid, cmdfunc->name, cmdfunc)) {
+ cbind = find_botwise_cmd_binding(botid, clientid, cmdfunc->name);
cbind->flags |= CMDFLAG_TEMPONARY_BIND;
}
}