diff --git a/doc/RELEASE-NOTES b/doc/RELEASE-NOTES index fb2d79ba9..694fdcde7 100644 --- a/doc/RELEASE-NOTES +++ b/doc/RELEASE-NOTES @@ -360,3 +360,16 @@ Server protocol such as SID, NICKv2, TKLEXT2, etc. (more precise information will follow) In particular this means UnrealIRCd 3.2.x will not link with 5.x. + +Client protocol +---------------- +TODO: expand with other new things / changes +* When a message is blocked, for whatever reason, we now use a generic + numeric response: :server 531 yourname targetname :reason for the block + This replaces all the various NOTICEs, ERR_NOCTCP, ERR_NONONREG, etc. + with just one single numeric. + The only other numerics that you may still encounter when PM'ing are + ERR_NOSUCHNICK, ERR_TOOMANYTARGETS and ERR_TARGETTOOFAST, which are + generic errors to any command involving targets. And ERR_SERVICESDOWN. + Note that channel messages already had a generic numeric for signaling + blocked messages for a very long time, ERR_CANNOTSENDTOCHAN. diff --git a/include/h.h b/include/h.h index fde7262de..64b26c76e 100644 --- a/include/h.h +++ b/include/h.h @@ -284,7 +284,7 @@ extern void sendto_ops_and_log(FORMAT_STRING(const char *pattern), ...) __attrib extern MODVAR int writecalls, writeb[]; extern int deliver_it(Client *cptr, char *str, int len, int *want_read); -extern int check_for_target_limit(Client *client, void *target, const char *name); +extern int target_limit_exceeded(Client *client, void *target, const char *name); extern char *canonize(char *buffer); extern ConfigItem_deny_dcc *dcc_isforbidden(Client *client, char *filename); extern ConfigItem_deny_dcc *dcc_isdiscouraged(Client *client, char *filename); @@ -311,7 +311,7 @@ extern int umode_allow_all(Client *client, int what); extern int umode_allow_unset(Client *client, int what); extern int umode_allow_opers(Client *client, int what); extern int umode_allow_none(Client *client, int what); -extern int umode_delete(char ch, long val); +extern int umode_delete(char ch, long val); extern void build_umode_string(Client *client, long old, long sendmask, char *umode_buf); extern void send_umode_out(Client *client, int show_to_user, long old); diff --git a/include/modules.h b/include/modules.h index 0545bb380..353af2ae8 100644 --- a/include/modules.h +++ b/include/modules.h @@ -931,7 +931,6 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long va #define HOOKTYPE_FREE_CLIENT 59 #define HOOKTYPE_FREE_USER 60 #define HOOKTYPE_PRE_CHANMSG 61 -#define HOOKTYPE_PRE_USERMSG 62 #define HOOKTYPE_KNOCK 63 #define HOOKTYPE_MODECHAR_ADD 64 #define HOOKTYPE_MODECHAR_DEL 65 @@ -973,6 +972,8 @@ extern void SavePersistentLongX(ModuleInfo *modinfo, char *varshortname, long va #define HOOKTYPE_PRE_LOCAL_QUIT_CHAN 102 #define HOOKTYPE_IDENT_LOOKUP 103 #define HOOKTYPE_CONFIGRUN_EX 104 +#define HOOKTYPE_CAN_SEND_TO_USER 105 +// FIXME: ^^ make this enums? or..? /* Adding a new hook here? * 1) Add the #define HOOKTYPE_.... with a new number @@ -1008,6 +1009,7 @@ int hooktype_remote_kick(Client *client, Client *victim, Channel *channel, Messa char *hooktype_pre_usermsg(Client *client, Client *to, char *text, int notice); int hooktype_usermsg(Client *client, Client *to, MessageTag *mtags, char *text, int notice); int hooktype_can_send_to_channel(Client *client, Channel *channel, Membership *member, char **text, char **errmsg, int notice); +int hooktype_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice); int hooktype_pre_chanmsg(Client *client, Channel *channel, MessageTag *mtags, char *text, int notice); int hooktype_chanmsg(Client *client, Channel *channel, int sendflags, int prefix, char *target, MessageTag *mtags, char *text, int notice); char *hooktype_pre_local_topic(Client *client, Channel *channel, char *topic); @@ -1145,11 +1147,11 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum ((hooktype == HOOKTYPE_INVITE) && !ValidateHook(hooktype_invite, func)) || \ ((hooktype == HOOKTYPE_CAN_JOIN) && !ValidateHook(hooktype_can_join, func)) || \ ((hooktype == HOOKTYPE_CAN_SEND_TO_CHANNEL) && !ValidateHook(hooktype_can_send_to_channel, func)) || \ + ((hooktype == HOOKTYPE_CAN_SEND_TO_USER) && !ValidateHook(hooktype_can_send_to_user, func)) || \ ((hooktype == HOOKTYPE_CAN_KICK) && !ValidateHook(hooktype_can_kick, func)) || \ ((hooktype == HOOKTYPE_FREE_CLIENT) && !ValidateHook(hooktype_free_client, func)) || \ ((hooktype == HOOKTYPE_FREE_USER) && !ValidateHook(hooktype_free_user, func)) || \ ((hooktype == HOOKTYPE_PRE_CHANMSG) && !ValidateHook(hooktype_pre_chanmsg, func)) || \ - ((hooktype == HOOKTYPE_PRE_USERMSG) && !ValidateHook(hooktype_pre_usermsg, func)) || \ ((hooktype == HOOKTYPE_KNOCK) && !ValidateHook(hooktype_knock, func)) || \ ((hooktype == HOOKTYPE_MODECHAR_ADD) && !ValidateHook(hooktype_modechar_add, func)) || \ ((hooktype == HOOKTYPE_MODECHAR_DEL) && !ValidateHook(hooktype_modechar_del, func)) || \ @@ -1276,6 +1278,7 @@ enum EfunctionType { EFUNC_TKL_CHARTOTYPE, EFUNC_TKL_TYPE_STRING, EFUNC_CAN_SEND_TO_CHANNEL, + EFUNC_CAN_SEND_TO_USER, EFUNC_BROADCAST_MD_GLOBALVAR, EFUNC_BROADCAST_MD_GLOBALVAR_CMD, EFUNC_TKL_IP_HASH, diff --git a/include/numeric.h b/include/numeric.h index 40978313c..fce426860 100644 --- a/include/numeric.h +++ b/include/numeric.h @@ -148,6 +148,7 @@ #define ERR_OPERONLY 520 #define ERR_LISTSYNTAX 521 +#define ERR_CANTSENDTOUSER 531 /* * Numberic replies from server commands. * These are currently in the range 200-399. diff --git a/include/proto.h b/include/proto.h index c7c0ca07e..8d5ea7283 100644 --- a/include/proto.h +++ b/include/proto.h @@ -23,7 +23,7 @@ #ifndef proto_h #define proto_h /* channel.c */ -extern int sendmodeto_one(Client *cptr, char *from, char *name, char *mode, char *param, time_t creationtime); +extern int sendmodeto_one(Client *cptr, char *from, char *name, char *mode, char *param, time_t creationtime); /* lusers.c */ extern void init_irccounts(void); @@ -54,7 +54,7 @@ extern EVENT(save_tunefile); extern void read_motd(const char *filename, MOTDFile *motd); /* s_user.c */ -extern int check_for_target_limit(Client *client, void *target, const char *name); +extern int target_limit_exceeded(Client *client, void *target, const char *name); extern void make_umodestr(void); extern char *get_mode_str(Client *acptr); diff --git a/src/err.c b/src/err.c index aa857bacb..a47a92be4 100644 --- a/src/err.c +++ b/src/err.c @@ -570,7 +570,7 @@ static char *replies[] = { /* 528 */ NULL, /* 529 */ NULL, /* 530 */ NULL, -/* 531 */ NULL, +/* 531 ERR_CANTSENDTOUSER */ "%s :%s", /* 532 */ NULL, /* 533 */ NULL, /* 534 */ NULL, diff --git a/src/modules/invite.c b/src/modules/invite.c index a8c0ad1de..81adaf72c 100644 --- a/src/modules/invite.c +++ b/src/modules/invite.c @@ -162,7 +162,7 @@ CMD_FUNC(cmd_invite) if (MyConnect(client)) { - if (check_for_target_limit(client, target, target->name)) + if (target_limit_exceeded(client, target, target->name)) return; if (!ValidatePermissionsForPath("immune:invite-flood",client,NULL,NULL,NULL)) diff --git a/src/modules/message.c b/src/modules/message.c index 6080bc4dc..d4dd53c41 100644 --- a/src/modules/message.c +++ b/src/modules/message.c @@ -29,6 +29,7 @@ CMD_FUNC(cmd_private); CMD_FUNC(cmd_notice); void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], int notice); int _can_send_to_channel(Client *client, Channel *channel, char **msgtext, char **errmsg, int notice); +int can_send_to_user(Client *client, Client *target, char **msgtext, char **errmsg, int notice); /* Place includes here */ #define MSG_PRIVATE "PRIVMSG" /* PRIV */ @@ -73,8 +74,9 @@ MOD_UNLOAD() return MOD_SUCCESS; } -static int check_dcc(Client *client, char *target, Client *targetcli, char *text); -static int check_dcc_soft(Client *from, Client *to, char *text); +char *get_dcc_filename(const char *text); +static int can_dcc(Client *client, char *target, Client *targetcli, char *filename, char **errmsg); +static int can_dcc_soft(Client *from, Client *to, char *filename, char **errmsg); #define CANPRIVMSG_CONTINUE 100 #define CANPRIVMSG_SEND 101 @@ -84,63 +86,74 @@ static int check_dcc_soft(Client *from, Client *to, char *text); * notice: 1 if notice, 0 if privmsg * text: Pointer to a pointer to a text [in, out] * cmd: Pointer to a pointer which contains the command to use [in, out] - * - * RETURN VALUES: - * CANPRIVMSG_CONTINUE: issue a 'continue' in target nickname list (aka: skip further processing this target) - * CANPRIVMSG_SEND: send the message (use text/newcmd!) - * Other: return with this value (can be anything) */ -static int can_privmsg(Client *client, Client *target, int notice, char **text, char **cmd) +int can_send_to_user(Client *client, Client *target, char **msgtext, char **errmsg, int notice) { int ret; + Hook *h; + int n; + static char errbuf[256]; + + *errmsg = NULL; if (IsVirus(client)) { - sendnotice(client, "You are only allowed to talk in '%s'", SPAMFILTER_VIRUSCHAN); - return CANPRIVMSG_CONTINUE; + ircsnprintf(errbuf, sizeof(errbuf), "You are only allowed to talk in '%s'", SPAMFILTER_VIRUSCHAN); + *errmsg = errbuf; + return 0; } - if (MyUser(client) && !strncasecmp(*text, "\001DCC", 4)) + if (**msgtext == '\001') { - ret = check_dcc(client, target->name, target, *text); - if (IsDead(client)) - return 0; - if (ret == 0) - return CANPRIVMSG_CONTINUE; - } - if (MyUser(target) && !strncasecmp(*text, "\001DCC", 4) && - !check_dcc_soft(client, target, *text)) - return CANPRIVMSG_CONTINUE; - - if (MyUser(client) && check_for_target_limit(client, target, target->name)) - return CANPRIVMSG_CONTINUE; - - if (!is_silenced(client, target)) - { - Hook *tmphook; - - if (!notice && MyConnect(client) && - target->user && target->user->away) - sendnumeric(client, RPL_AWAY, target->name, - target->user->away); - - if (MyUser(client) && match_spamfilter(client, *text, (notice ? SPAMF_USERNOTICE : SPAMF_USERMSG), target->name, 0, NULL)) - return 0; - - for (tmphook = Hooks[HOOKTYPE_PRE_USERMSG]; tmphook; tmphook = tmphook->next) { - *text = (*(tmphook->func.pcharfunc))(client, target, *text, notice); - if (!*text) - break; + char *filename = get_dcc_filename(*msgtext); + if (filename) + { + if (MyUser(client) && !can_dcc(client, target->name, target, filename, errmsg)) + return 0; + if (MyUser(target) && !can_dcc_soft(client, target, filename, errmsg)) + return 0; } - if (!*text) - return CANPRIVMSG_CONTINUE; - - return CANPRIVMSG_SEND; - } else { - /* Silenced */ - RunHook3(HOOKTYPE_SILENCED, client, target, notice); } - return CANPRIVMSG_CONTINUE; + + if (MyUser(client) && target_limit_exceeded(client, target, target->name)) + { + /* target_limit_exceeded() is an exception, in the sense that + * it will send a different numeric. So we don't set errmsg. + */ + return 0; + } + + if (is_silenced(client, target)) + { + RunHook3(HOOKTYPE_SILENCED, client, target, notice); + /* Silently discarded, no error message */ + return 0; + } + + // Possible FIXME: make match_spamfilter also use errmsg, or via a wrapper? or use same numeric? + if (MyUser(client) && match_spamfilter(client, *msgtext, (notice ? SPAMF_USERNOTICE : SPAMF_USERMSG), target->name, 0, NULL)) + return 0; + + n = HOOK_CONTINUE; + for (h = Hooks[HOOKTYPE_CAN_SEND_TO_USER]; h; h = h->next) + { + n = (*(h->func.intfunc))(client, target, msgtext, errmsg, notice); + if (n == HOOK_DENY) + { + if (!*errmsg) + { + ircd_log(LOG_ERROR, "Module %s did not set errmsg!!!", h->owner->header->name); + abort(); + } + return 0; + } + } + + /* This may happen, if nothing is left to send anymore (don't send empty messages) */ + if (!*msgtext) + return 0; + + return 1; } /* @@ -158,7 +171,7 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], { Client *target; Channel *channel; - char *nick, *p, *p2, *pc, *text, *errmsg, *newcmd; + char *nick, *p, *p2, *pc, *text, *errmsg; int prefix = 0; char pfixchan[CHANNELLEN + 4]; int ret; @@ -297,13 +310,18 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], } } - if (MyUser(client) && (*parv[2] == 1)) + if (MyUser(client) && (*parv[2] == '\001')) { - ret = check_dcc(client, channel->chname, NULL, parv[2]); - if (IsDead(client)) - return; - if (ret == 0) + char *errmsg = NULL; + char *filename = get_dcc_filename(parv[2]); + if (filename && !can_dcc(client, channel->chname, NULL, filename, &errmsg)) + { + if (IsDead(client)) + return; + if (!IsDead(client) && !notice) + sendnumeric(client, ERR_CANNOTSENDTOCHAN, channel->chname, errmsg, p2); continue; + } } if (IsVirus(client) && strcasecmp(channel->chname, SPAMFILTER_VIRUSCHAN)) @@ -399,30 +417,37 @@ void cmd_message(Client *client, MessageTag *recv_mtags, int parc, char *parv[], target = hash_find_nickatserver(nick, NULL); if (target) { + char *errmsg = NULL; text = parv[2]; - newcmd = cmd; - ret = can_privmsg(client, target, notice, &text, &newcmd); - if (IsDead(client)) - return; - if (ret == CANPRIVMSG_SEND) + if (!can_send_to_user(client, target, &text, &errmsg, notice)) { + /* Message is discarded */ + if (IsDead(client)) + return; + if (!notice && errmsg) + sendnumeric(client, ERR_CANTSENDTOUSER, target->name, errmsg); + } else + { + /* We may send the message */ MessageTag *mtags = NULL; + + /* Inform sender that recipient is away, if this is so */ + if (!notice && MyConnect(client) && target->user && target->user->away) + sendnumeric(client, RPL_AWAY, target->name, target->user->away); + new_message(client, recv_mtags, &mtags); labeled_response_inhibit = 1; sendto_prefix_one(target, client, mtags, ":%s %s %s :%s", CHECKPROTO(target->direction, PROTO_SID) ? ID(client) : client->name, - newcmd, + cmd, (MyUser(target) ? target->name : nick), text); labeled_response_inhibit = 0; RunHook5(HOOKTYPE_USERMSG, client, target, mtags, text, notice); free_message_tags(mtags); continue; - } else - if (ret == CANPRIVMSG_CONTINUE) - continue; - else - return; + } + continue; /* Message has been delivered or rejected, continue with next target */ } /* If nick@server -and- the @server portion was set::services-server then send a special message */ @@ -468,9 +493,9 @@ CMD_FUNC(cmd_notice) */ char *dcc_displayfile(char *f) { -static char buf[512]; -char *i, *o = buf; -size_t n = strlen(f); + static char buf[512]; + char *i, *o = buf; + size_t n = strlen(f); if (n < 300) { @@ -500,80 +525,95 @@ size_t n = strlen(f); return buf; } +char *get_dcc_filename(const char *text) +{ + static char filename[BUFSIZE+1]; + char *end; + int size_string; + + if (*text != '\001') + return 0; + + if (!strncasecmp(text+1, "DCC SEND ", 9)) + text = text + 10; + else if (!strncasecmp(text+1, "DCC RESUME ", 11)) + text = text + 12; + else + return 0; + + for (; *text == ' '; text++); /* skip leading spaces */ + + if (*text == '"' && *(text+1)) + end = strchr(text+1, '"'); + else + end = strchr(text, ' '); + + if (!end || (end < text)) + return 0; + + size_string = (int)(end - text); + + if (!size_string || (size_string > (BUFSIZE - 1))) + return 0; + + strlcpy(filename, text, size_string+1); + return filename; +} + /** Checks if a DCC SEND is allowed. - * @param client Sending client + * @param client Sending client * @param target Target name (user or channel) * @param targetcli Target client (NULL in case of channel!) * @param text The entire message * @returns 1 if DCC SEND allowed, 0 if rejected */ -static int check_dcc(Client *client, char *target, Client *targetcli, char *text) +static int can_dcc(Client *client, char *target, Client *targetcli, char *filename, char **errmsg) { - char *ctcp; ConfigItem_deny_dcc *fl; - char *end, realfile[BUFSIZE]; + static char errbuf[256]; int size_string, ret; - if ((*text != 1) || ValidatePermissionsForPath("immune:dcc",client,targetcli,NULL,NULL) || (targetcli && ValidatePermissionsForPath("self:getbaddcc",targetcli,NULL,NULL,NULL))) + /* User (IRCOp) may bypass send restrictions */ + if (ValidatePermissionsForPath("immune:dcc",client,targetcli,NULL,NULL)) return 1; - ctcp = &text[1]; - /* Most likely a DCC send .. */ - if (!strncasecmp(ctcp, "DCC SEND ", 9)) - ctcp = text + 10; - else if (!strncasecmp(ctcp, "DCC RESUME ", 11)) - ctcp = text + 12; - else - return 1; /* something else, allow */ + /* User (IRCOp) likes to receive bad dcc's */ + if (targetcli && ValidatePermissionsForPath("self:getbaddcc",targetcli,NULL,NULL,NULL)) + return 1; + /* Check if user is already blocked (from the past) */ if (IsDCCBlock(client)) { - sendnotice(client, "*** You are blocked from sending files as you have tried to " - "send a forbidden file - reconnect to regain ability to send"); + *errmsg = "*** You are blocked from sending files as you have tried to " + "send a forbidden file - reconnect to regain ability to send"; return 0; } - for (; *ctcp == ' '; ctcp++); /* skip leading spaces */ - if (*ctcp == '"' && *(ctcp+1)) - end = strchr(ctcp+1, '"'); - else - end = strchr(ctcp, ' '); + if (match_spamfilter(client, filename, SPAMF_DCC, target, 0, NULL)) + return 0; - /* check if it was fake.. just pass it along then .. */ - if (!end || (end < ctcp)) - return 1; /* allow */ - - size_string = (int)(end - ctcp); - - if (!size_string || (size_string > (BUFSIZE - 1))) - return 1; /* allow */ - - strlcpy(realfile, ctcp, size_string+1); - - if (match_spamfilter(client, realfile, SPAMF_DCC, target, 0, NULL)) - return 0; /* deny */ - - if ((fl = dcc_isforbidden(client, realfile))) + if ((fl = dcc_isforbidden(client, filename))) { - char *displayfile = dcc_displayfile(realfile); - sendnumericfmt(client, - RPL_TEXT, "*** Cannot DCC SEND file %s to %s (%s)", displayfile, target, fl->reason); - sendnotice(client, "*** You have been blocked from sending files, reconnect to regain permission to send files"); + char *displayfile = dcc_displayfile(filename); + + RunHook5(HOOKTYPE_DCC_DENIED, client, target, filename, displayfile, fl); + + ircsnprintf(errbuf, sizeof(errbuf), "Cannot DCC SEND file: %s", fl->reason); + *errmsg = errbuf; SetDCCBlock(client); - - RunHook5(HOOKTYPE_DCC_DENIED, client, target, realfile, displayfile, fl); - - return 0; /* block */ + return 0; } /* Channel dcc (???) and discouraged? just block */ - if (!targetcli && ((fl = dcc_isdiscouraged(client, realfile)))) + if (!targetcli && ((fl = dcc_isdiscouraged(client, filename)))) { - char *displayfile = dcc_displayfile(realfile); - sendnumericfmt(client, RPL_TEXT, "*** Cannot DCC SEND file %s to %s (%s)", displayfile, target, fl->reason); - return 0; /* block */ + ircsnprintf(errbuf, sizeof(errbuf), "Cannot DCC SEND file: %s", fl->reason); + *errmsg = errbuf; + return 0; } - return 1; /* allowed */ + + /* If we get here, the file is allowed */ + return 1; } /** Checks if a DCC is allowed by DCCALLOW rules (only SOFT bans are checked). @@ -585,63 +625,46 @@ static int check_dcc(Client *client, char *target, Client *targetcli, char *text * 1: allowed * 0: block */ -static int check_dcc_soft(Client *from, Client *to, char *text) +static int can_dcc_soft(Client *from, Client *to, char *filename, char **errmsg) { - char *ctcp; ConfigItem_deny_dcc *fl; - char *end, realfile[BUFSIZE]; - int size_string; + char *displayfile; + static char errbuf[256]; - if ((*text != 1) || ValidatePermissionsForPath("immune:dcc",from,to,NULL,NULL)|| ValidatePermissionsForPath("self:getbaddcc",to,NULL,NULL,NULL)) + /* User (IRCOp) may bypass send restrictions */ + if (ValidatePermissionsForPath("immune:dcc",from,to,NULL,NULL)) return 1; - ctcp = &text[1]; - /* Most likely a DCC send .. */ - if (!strncasecmp(ctcp, "DCC SEND ", 9)) - ctcp = text + 10; - else - return 1; /* something else, allow */ + /* User (IRCOp) likes to receive bad dcc's */ + if (ValidatePermissionsForPath("self:getbaddcc",to,NULL,NULL,NULL)) + return 1; - if (*ctcp == '"' && *(ctcp+1)) - end = strchr(ctcp+1, '"'); - else - end = strchr(ctcp, ' '); + /* On the 'soft' blocklist ? */ + if (!(fl = dcc_isdiscouraged(from, filename))) + return 1; /* No, so is OK */ - /* check if it was fake.. just pass it along then .. */ - if (!end || (end < ctcp)) - return 1; /* allow */ + /* If on DCCALLOW list then the user is OK with it */ + if (on_dccallow_list(to, from)) + return 1; - size_string = (int)(end - ctcp); + /* Soft-blocked */ + displayfile = dcc_displayfile(filename); - if (!size_string || (size_string > (BUFSIZE - 1))) - return 1; /* allow */ + ircsnprintf(errbuf, sizeof(errbuf), "Cannot DCC SEND file: %s", fl->reason); + *errmsg = errbuf; - strlcpy(realfile, ctcp, size_string+1); - - if ((fl = dcc_isdiscouraged(from, realfile))) + /* Inform target ('to') about the /DCCALLOW functionality */ + sendnotice(to, "%s (%s@%s) tried to DCC SEND you a file named '%s', the request has been blocked.", + from->name, from->user->username, GetHost(from), displayfile); + if (!IsDCCNotice(to)) { - if (!on_dccallow_list(to, from)) - { - char *displayfile = dcc_displayfile(realfile); - sendnumericfmt(from, - RPL_TEXT, "*** Cannot DCC SEND file %s to %s (%s)", displayfile, to->name, fl->reason); - sendnotice(from, "User %s is currently not accepting DCC SENDs with such a filename/filetype from you. " - "Your file %s was not sent.", to->name, displayfile); - sendnotice(to, "%s (%s@%s) tried to DCC SEND you a file named '%s', the request has been blocked.", - from->name, from->user->username, GetHost(from), displayfile); - if (!IsDCCNotice(to)) - { - SetDCCNotice(to); - sendnotice(to, "Files like these might contain malicious content (viruses, trojans). " - "Therefore, you must explicitly allow anyone that tries to send you such files."); - sendnotice(to, "If you trust %s, and want him/her to send you this file, you may obtain " - "more information on using the dccallow system by typing '/DCCALLOW HELP'", from->name); - } - return 0; - } + SetDCCNotice(to); + sendnotice(to, "Files like these might contain malicious content (viruses, trojans). " + "Therefore, you must explicitly allow anyone that tries to send you such files."); + sendnotice(to, "If you trust %s, and want him/her to send you this file, you may obtain " + "more information on using the dccallow system by typing '/DCCALLOW HELP'", from->name); } - - return 1; /* allowed */ + return 0; } /* Taken from xchat by Peter Zelezny @@ -649,7 +672,8 @@ static int check_dcc_soft(Client *from, Client *to, char *text) * RGB color stripping support added -- codemastr */ -char *_StripColors(unsigned char *text) { +char *_StripColors(unsigned char *text) +{ int i = 0, len = strlen(text), save_len=0; char nc = 0, col = 0, rgb = 0, *save_text=NULL; static unsigned char new_str[4096]; @@ -906,13 +930,11 @@ int _can_send_to_channel(Client *client, Channel *channel, char **msgtext, char i = (*(h->func.intfunc))(client, channel, lp, msgtext, errmsg, notice); if (i != HOOK_CONTINUE) { -#ifdef DEBUGMODE if (!*errmsg) { ircd_log(LOG_ERROR, "Module %s did not set errmsg!!!", h->owner->header->name); abort(); } -#endif break; } } diff --git a/src/modules/restrict-commands.c b/src/modules/restrict-commands.c index e2e89737a..a0537c7a8 100644 --- a/src/modules/restrict-commands.c +++ b/src/modules/restrict-commands.c @@ -52,8 +52,8 @@ RestrictedCommand *find_restrictions_byconftag(char *conftag); int rcmd_configtest(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type); int rcmd_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, int notice); -char *rcmd_hook_preusermsg(Client *client, Client *to, char *text, int notice); -char *rcmd_hook_wrapper(Client *client, char *text, int notice, char *display, char *conftag); +int rcmd_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice); +int rcmd_block_message(Client *client, char *text, int notice, char **errmsg, char *display, char *conftag); CMD_OVERRIDE_FUNC(rcmd_override); // Globals @@ -82,7 +82,7 @@ MOD_INIT() // Due to the nature of PRIVMSG/NOTICE we're gonna need to hook into PRE_* stuff instead of using command overrides HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, -1000000, rcmd_can_send_to_channel); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_USERMSG, -1000000, rcmd_hook_preusermsg); + HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_USER, -1000000, rcmd_can_send_to_user); return MOD_SUCCESS; } @@ -294,7 +294,8 @@ int rcmd_configrun(ConfigFile *cf, ConfigEntry *ce, int type) return 1; } -int rcmd_canbypass(Client *client, RestrictedCommand *rcmd) { +int rcmd_canbypass(Client *client, RestrictedCommand *rcmd) +{ if (!client || !rcmd) return 1; if (rcmd->exempt_identified && IsLoggedIn(client)) @@ -308,45 +309,56 @@ int rcmd_canbypass(Client *client, RestrictedCommand *rcmd) { int rcmd_can_send_to_channel(Client *client, Channel *channel, Membership *lp, char **msg, char **errmsg, int notice) { - if (!rcmd_hook_wrapper(client, *msg, notice, "channel", (notice ? "channel-notice" : "channel-message"))) + if (rcmd_block_message(client, *msg, notice, errmsg, "channel", (notice ? "channel-notice" : "channel-message"))) return HOOK_DENY; - // FIXME ^^^^ may not send notices from the wrapper, but will convert when preusermsg is done as well + return HOOK_CONTINUE; } -char *rcmd_hook_preusermsg(Client *client, Client *to, char *text, int notice) +int rcmd_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice) { // Need a few extra exceptions for user messages only =] - if ((client == to) || IsULine(to)) - return text; - return rcmd_hook_wrapper(client, text, notice, "user", (notice ? "private-notice" : "private-message")); + if ((client == target) || IsULine(target)) + return HOOK_CONTINUE; /* bypass/exempt */ + + if (rcmd_block_message(client, *text, notice, errmsg, "user", (notice ? "private-notice" : "private-message"))) + return HOOK_DENY; + + return HOOK_CONTINUE; } -char *rcmd_hook_wrapper(Client *client, char *text, int notice, char *display, char *conftag) +int rcmd_block_message(Client *client, char *text, int notice, char **errmsg, char *display, char *conftag) { RestrictedCommand *rcmd; + static char errbuf[256]; // Let's allow non-local users, opers and U:Lines early =] if (!MyUser(client) || !client->local || IsOper(client) || IsULine(client)) - return text; + return 0; rcmd = find_restrictions_byconftag(conftag); if (rcmd) { if (rcmd->disable) { - sendnotice(client, "Sending of %ss to %ss been disabled by the network administrators", (notice ? "notice" : "message"), display); - return NULL; + ircsnprintf(errbuf, sizeof(errbuf), + "Sending of %ss to %ss been disabled by the network administrators", + (notice ? "notice" : "message"), display); + *errmsg = errbuf; + return 1; } if (!rcmd_canbypass(client, rcmd)) { - sendnotice(client, "You cannot send %ss to %ss until you've been connected for %ld seconds or more", (notice ? "notice" : "message"), display, rcmd->connect_delay); - return NULL; + ircsnprintf(errbuf, sizeof(errbuf), + "You cannot send %ss to %ss until you've been connected for %ld seconds or more", + (notice ? "notice" : "message"), display, rcmd->connect_delay); + *errmsg = errbuf; + return 1; } } // No restrictions apply, process command as normal =] - return text; + return 0; } CMD_OVERRIDE_FUNC(rcmd_override) diff --git a/src/modules/tkl.c b/src/modules/tkl.c index 3c29589b7..7ca894abe 100644 --- a/src/modules/tkl.c +++ b/src/modules/tkl.c @@ -4385,16 +4385,27 @@ int _match_spamfilter(Client *client, char *str_in, int target, char *destinatio { case SPAMF_USERMSG: case SPAMF_USERNOTICE: - sendnotice(client, "Message to %s blocked: %s", destination, reason); + { + char errmsg[512]; + ircsnprintf(errmsg, sizeof(errmsg), "Message blocked: %s", reason); + sendnumeric(client, ERR_CANTSENDTOUSER, destination, errmsg); break; - case SPAMF_CHANMSG: + } case SPAMF_CHANNOTICE: + break; /* no replies to notices */ + case SPAMF_CHANMSG: + { sendto_one(client, NULL, ":%s 404 %s %s :Message blocked: %s", me.name, client->name, destination, reason); break; + } case SPAMF_DCC: - sendnotice(client, "DCC to %s blocked: %s", destination, reason); + { + char errmsg[512]; + ircsnprintf(errmsg, sizeof(errmsg), "DCC blocked: %s", reason); + sendnumeric(client, ERR_CANTSENDTOUSER, destination, errmsg); break; + } case SPAMF_AWAY: /* hack to deal with 'after-away-was-set-filters' */ if (client->user->away && !strcmp(str_in, client->user->away)) diff --git a/src/modules/usermodes/censor.c b/src/modules/usermodes/censor.c index dba0f3742..68452abeb 100644 --- a/src/modules/usermodes/censor.c +++ b/src/modules/usermodes/censor.c @@ -20,7 +20,7 @@ long UMODE_CENSOR = 0L; #define IsCensored(x) (x->umodes & UMODE_CENSOR) -char *censor_pre_usermsg(Client *client, Client *target, char *text, int notice); +int censor_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice); int censor_config_test(ConfigFile *, ConfigEntry *, int, int *); int censor_config_run(ConfigFile *, ConfigEntry *, int); @@ -46,7 +46,7 @@ MOD_INIT() UmodeAdd(modinfo->handle, 'G', UMODE_GLOBAL, 0, NULL, &UMODE_CENSOR); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_USERMSG, 0, censor_pre_usermsg); + HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_USER, 0, censor_can_send_to_user); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, censor_config_run); return MOD_SUCCESS; @@ -237,22 +237,21 @@ char *stripbadwords_message(char *str, int *blocked) return stripbadwords(str, conf_badword_message, blocked); } -char *censor_pre_usermsg(Client *client, Client *target, char *text, int notice) +int censor_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice) { -int blocked; + int blocked = 0; if (MyUser(client) && IsCensored(target)) { - text = stripbadwords_message(text, &blocked); + *text = stripbadwords_message(*text, &blocked); if (blocked) { - if (!notice) - sendnumeric(client, ERR_NOSWEAR, target->name); - return NULL; + *errmsg = "User does not accept private messages containing swearing"; + return HOOK_DENY; } } - return text; + return HOOK_CONTINUE; } // TODO: when stats is modular, make it call this for badwords diff --git a/src/modules/usermodes/noctcp.c b/src/modules/usermodes/noctcp.c index 0874aaa29..92498091a 100644 --- a/src/modules/usermodes/noctcp.c +++ b/src/modules/usermodes/noctcp.c @@ -34,7 +34,7 @@ long UMODE_NOCTCP = 0L; #define IsNoCTCP(client) (client->umodes & UMODE_NOCTCP) -char *noctcp_preusermsg(Client *client, Client *target, char *text, int notice); +int noctcp_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice); MOD_TEST() { @@ -47,7 +47,7 @@ CmodeInfo req; UmodeAdd(modinfo->handle, 'T', UMODE_GLOBAL, 0, NULL, &UMODE_NOCTCP); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_USERMSG, 0, noctcp_preusermsg); + HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_USER, 0, noctcp_can_send_to_user); MARK_AS_OFFICIAL_MODULE(modinfo); return MOD_SUCCESS; @@ -74,12 +74,12 @@ static int IsACTCP(char *s) return 0; } -char *noctcp_preusermsg(Client *client, Client *target, char *text, int notice) +int noctcp_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice) { - if (MyUser(client) && !notice && IsNoCTCP(target) && !IsOper(client) && IsACTCP(text)) + if (MyUser(client) && !notice && IsNoCTCP(target) && !IsOper(client) && IsACTCP(*text)) { - sendnumeric(client, ERR_NOCTCP, target->name); - return NULL; + *errmsg = "User does not accept CTCPs"; + return HOOK_DENY; } - return text; + return HOOK_CONTINUE; } diff --git a/src/modules/usermodes/privdeaf.c b/src/modules/usermodes/privdeaf.c index 625b098f8..a788c0053 100644 --- a/src/modules/usermodes/privdeaf.c +++ b/src/modules/usermodes/privdeaf.c @@ -17,7 +17,7 @@ ModuleHeader MOD_HEADER static long UMODE_PRIVDEAF = 0; static Umode *UmodePrivdeaf = NULL; -char *privdeaf_checkmsg(Client *, Client *, char *, int); +int privdeaf_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice); MOD_INIT() { @@ -32,7 +32,7 @@ MOD_INIT() return MOD_FAILED; } - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_USERMSG, 0, privdeaf_checkmsg); + HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_USER, 0, privdeaf_can_send_to_user); return MOD_SUCCESS; } @@ -47,13 +47,13 @@ MOD_UNLOAD() return MOD_SUCCESS; } -char *privdeaf_checkmsg(Client *client, Client *target, char *text, int notice) +int privdeaf_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice) { if ((target->umodes & UMODE_PRIVDEAF) && !IsOper(client) && !IsULine(client) && !IsServer(client) && (client != target)) { - sendnotice(client, "Message to '%s' not delivered: User does not accept private messages", target->name); - return NULL; - } else - return text; + *errmsg = "User does not accept private messages"; + return HOOK_DENY; + } + return HOOK_CONTINUE; } diff --git a/src/modules/usermodes/regonlymsg.c b/src/modules/usermodes/regonlymsg.c index 7109b55da..bc9cc7847 100644 --- a/src/modules/usermodes/regonlymsg.c +++ b/src/modules/usermodes/regonlymsg.c @@ -35,13 +35,13 @@ ModuleHeader MOD_HEADER long UMODE_REGONLYMSG = 0L; /* Forward declarations */ -char *regonlymsg_pre_usermsg(Client *client, Client *target, char *text, int notice); +int regonlymsg_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice); MOD_INIT() { UmodeAdd(modinfo->handle, 'R', UMODE_GLOBAL, 0, umode_allow_all, &UMODE_REGONLYMSG); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_USERMSG, 0, regonlymsg_pre_usermsg); + HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_USER, 0, regonlymsg_can_send_to_user); MARK_AS_OFFICIAL_MODULE(modinfo); return MOD_SUCCESS; @@ -57,17 +57,16 @@ MOD_UNLOAD() return MOD_SUCCESS; } -char *regonlymsg_pre_usermsg(Client *client, Client *target, char *text, int notice) +int regonlymsg_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice) { if (IsRegOnlyMsg(target) && !IsServer(client) && !IsULine(client) && !IsLoggedIn(client)) { if (ValidatePermissionsForPath("client:override:message:regonlymsg",client,target,NULL,text)) - return text; /* TODO: this is actually an override */ + return HOOK_CONTINUE; /* bypass this restriction */ - sendnumeric(client, ERR_NONONREG, target->name); - - return NULL; /* Block the message */ + *errmsg = "You must identify to a registered nick to private message this user"; + return HOOK_DENY; } - return text; + return HOOK_CONTINUE; } diff --git a/src/modules/usermodes/secureonlymsg.c b/src/modules/usermodes/secureonlymsg.c index 0bf7a1d43..ecc818368 100644 --- a/src/modules/usermodes/secureonlymsg.c +++ b/src/modules/usermodes/secureonlymsg.c @@ -36,13 +36,13 @@ ModuleHeader MOD_HEADER long UMODE_SECUREONLYMSG = 0L; /* Forward declarations */ -char *secureonlymsg_pre_usermsg(Client *client, Client *target, char *text, int notice); +int secureonlymsg_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice); MOD_INIT() { UmodeAdd(modinfo->handle, 'Z', UMODE_GLOBAL, 0, umode_allow_all, &UMODE_SECUREONLYMSG); - HookAddPChar(modinfo->handle, HOOKTYPE_PRE_USERMSG, 0, secureonlymsg_pre_usermsg); + HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_USER, 0, secureonlymsg_can_send_to_user); MARK_AS_OFFICIAL_MODULE(modinfo); return MOD_SUCCESS; @@ -58,36 +58,29 @@ MOD_UNLOAD() return MOD_SUCCESS; } -char *secureonlymsg_pre_usermsg(Client *client, Client *target, char *text, int notice) +int secureonlymsg_can_send_to_user(Client *client, Client *target, char **text, char **errmsg, int notice) { if (IsSecureOnlyMsg(target) && !IsServer(client) && !IsULine(client) && !IsSecureConnect(client)) { if (ValidatePermissionsForPath("client:override:message:secureonlymsg",client,target,NULL,text)) - return text; /* TODO: this is actually an override */ + return HOOK_CONTINUE; /* bypass this restriction */ - /* A numeric is preferred to indicate the user cannot message. - * 492 is ERR_NOCTCP but apparently is also used by some other ircd(s) as a - * general "cannot send message" numeric (similar to the generic - * ERR_CANNOTSENDTOCHAN for channel messaging). - */ - sendto_one(client, NULL, ":%s 492 %s :Cannot send to user %s (You must be connected via SSL/TLS to message this user)", - me.name, client->name, target->name); - - return NULL; /* Block the message */ + *errmsg = "You must be connected via SSL/TLS to message this user"; + return HOOK_DENY; } else if (IsSecureOnlyMsg(client) && !IsSecureConnect(target) && !IsULine(target)) { if (ValidatePermissionsForPath("client:override:message:secureonlymsg",client,target,NULL,text)) - return text; /* TODO: this is actually an override */ + return HOOK_CONTINUE; /* bypass this restriction */ /* Similar to above but in this case we are +Z and are trying to message * an SSL user (who does not have +Z set, note the 'else'). This does not * make sense since they could never message back to us. Better block the * message than leave the user confused. */ - sendto_one(client, NULL, ":%s 492 %s :Cannot send to user %s (You have user mode +Z set but are not connected via SSL/TLS)", - me.name, client->name, target->name); + *errmsg = "Recipient is not connected via SSL/TLS and you are +Z"; + return HOOK_DENY; } - return text; + return HOOK_CONTINUE; } diff --git a/src/user.c b/src/user.c index 2bb38cc76..be417f1cf 100644 --- a/src/user.c +++ b/src/user.c @@ -173,19 +173,20 @@ unsigned char hash_target(void *target) return (unsigned char)((v >> 17) ^ (v >> 9)); } -/** check_for_target_limit +/** target_limit_exceeded * @param client The client. * @param target The target client * @param name The name of the target client (used in the error message) * @retval Returns 1 if too many targets were addressed (do not send!), 0 if ok to send. */ -int check_for_target_limit(Client *client, void *target, const char *name) +int target_limit_exceeded(Client *client, void *target, const char *name) { u_char hash = hash_target(target); int i; if (ValidatePermissionsForPath("immune:target-limit",client,NULL,NULL,NULL)) return 0; + if (client->local->targets[0] == hash) return 0; @@ -206,8 +207,7 @@ int check_for_target_limit(Client *client, void *target, const char *name) client->local->nexttarget += 2; /* punish them some more */ client->local->since += 2; /* lag them up as well */ - sendnumeric(client, ERR_TARGETTOOFAST, - name, client->local->nexttarget - TStime()); + sendnumeric(client, ERR_TARGETTOOFAST, name, client->local->nexttarget - TStime()); return 1; }