]> jfr.im git - irc/rqf/shadowircd.git/blobdiff - modules/core/m_message.c
Backed out changeset 3097ade953f5
[irc/rqf/shadowircd.git] / modules / core / m_message.c
index 96da9d2edb15c36e33546c29fc9a959e4e3ea309..6f931173293e370359bcac731f63487ffbf107e7 100644 (file)
@@ -21,7 +21,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
  *  USA
  *
- *  $Id: m_message.c 3173 2007-01-31 23:57:18Z jilles $
  */
 
 #include "stdinc.h"
 #include "parse.h"
 #include "modules.h"
 #include "channel.h"
-#include "irc_string.h"
+#include "match.h"
 #include "hash.h"
 #include "class.h"
 #include "msg.h"
 #include "packet.h"
 #include "send.h"
-#include "event.h"
-#include "patricia.h"
 #include "s_newconf.h"
+#include "s_stats.h"
+#include "tgchange.h"
+#include "inline/stringops.h"
+#include "irc_dictionary.h"
 
 static int m_message(int, const char *, struct Client *, struct Client *, int, const char **);
 static int m_privmsg(struct Client *, struct Client *, int, const char **);
 static int m_notice(struct Client *, struct Client *, int, const char **);
 
 static void expire_tgchange(void *unused);
+static struct ev_entry *expire_tgchange_event;
 
 static int
 modinit(void)
 {
-       eventAddIsh("expire_tgchange", expire_tgchange, NULL, 300);
+       expire_tgchange_event = rb_event_addish("expire_tgchange", expire_tgchange, NULL, 300);
        expire_tgchange(NULL);
        return 0;
 }
@@ -62,7 +64,7 @@ modinit(void)
 static void
 moddeinit(void)
 {
-       eventDelete(expire_tgchange, NULL);
+       rb_event_delete(expire_tgchange_event);
 }
 
 struct Message privmsg_msgtab = {
@@ -92,12 +94,15 @@ static int build_target_list(int p_or_n, const char *command,
 static int flood_attack_client(int p_or_n, struct Client *source_p, struct Client *target_p);
 static int flood_attack_channel(int p_or_n, struct Client *source_p,
                                struct Channel *chptr, char *chname);
-static struct Client *find_userhost(const char *, const char *, int *);
+
+/* Fifteen seconds should be plenty for a client to reply a ctcp */
+#define LARGE_CTCP_TIME 15
 
 #define ENTITY_NONE    0
 #define ENTITY_CHANNEL 1
-#define ENTITY_CHANOPS_ON_CHANNEL 2
-#define ENTITY_CLIENT  3
+#define ENTITY_CHANNEL_OPMOD 2
+#define ENTITY_CHANOPS_ON_CHANNEL 3
+#define ENTITY_CLIENT  4
 
 static struct entity targets[512];
 static int ntargets = 0;
@@ -108,6 +113,11 @@ static void msg_channel(int p_or_n, const char *command,
                        struct Client *client_p,
                        struct Client *source_p, struct Channel *chptr, const char *text);
 
+static void msg_channel_opmod(int p_or_n, const char *command,
+                             struct Client *client_p,
+                             struct Client *source_p, struct Channel *chptr,
+                             const char *text);
+
 static void msg_channel_flags(int p_or_n, const char *command,
                              struct Client *client_p,
                              struct Client *source_p,
@@ -203,6 +213,11 @@ m_message(int p_or_n,
                                    (struct Channel *) targets[i].ptr, parv[2]);
                        break;
 
+               case ENTITY_CHANNEL_OPMOD:
+                       msg_channel_opmod(p_or_n, command, client_p, source_p,
+                                  (struct Channel *) targets[i].ptr, parv[2]);
+                       break;
+
                case ENTITY_CHANOPS_ON_CHANNEL:
                        msg_channel_flags(p_or_n, command, client_p, source_p,
                                          (struct Channel *) targets[i].ptr,
@@ -250,7 +265,7 @@ build_target_list(int p_or_n, const char *command, struct Client *client_p,
 
        ntargets = 0;
 
-       for(nick = strtoken(&p, target_list, ","); nick; nick = strtoken(&p, NULL, ","))
+       for(nick = rb_strtok_r(target_list, ",", &p); nick; nick = rb_strtok_r(NULL, ",", &p))
        {
                char *with_prefix;
                /*
@@ -349,7 +364,9 @@ build_target_list(int p_or_n, const char *command, struct Client *client_p,
                                if(!IsServer(source_p) && !IsService(source_p) && !is_chanop_voiced(msptr))
                                {
                                        sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
-                                                  me.name, source_p->name, with_prefix);
+                                                  get_id(&me, source_p),
+                                                  get_id(source_p, source_p),
+                                                  with_prefix);
                                        return (-1);
                                }
 
@@ -375,6 +392,32 @@ build_target_list(int p_or_n, const char *command, struct Client *client_p,
                        continue;
                }
 
+               if(IsServer(client_p) && *nick == '=' && nick[1] == '#')
+               {
+                       nick++;
+                       if((chptr = find_channel(nick)) != NULL)
+                       {
+                               if(!duplicate_ptr(chptr))
+                               {
+                                       if(ntargets >= ConfigFileEntry.max_targets)
+                                       {
+                                               sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
+                                                          me.name, source_p->name, nick);
+                                               return (1);
+                                       }
+                                       targets[ntargets].ptr = (void *) chptr;
+                                       targets[ntargets++].type = ENTITY_CHANNEL_OPMOD;
+                               }
+                       }
+
+                       /* non existant channel */
+                       else if(p_or_n != NOTICE)
+                               sendto_one_numeric(source_p, ERR_NOSUCHNICK,
+                                                  form_str(ERR_NOSUCHNICK), nick);
+
+                       continue;
+               }
+
                if(strchr(nick, '@') || (IsOper(source_p) && (*nick == '$')))
                {
                        handle_special(p_or_n, command, client_p, source_p, nick, text);
@@ -441,17 +484,40 @@ msg_channel(int p_or_n, const char *command,
 {
        int result;
        char text2[BUFSIZE];
+       size_t contor;
+       int caps = 0;
+       int len = 0;
+       struct membership *msptr = find_channel_membership(chptr, source_p);
+       struct Metadata *md;
 
        if(MyClient(source_p))
        {
                /* idle time shouldnt be reset by notices --fl */
                if(p_or_n != NOTICE)
-                       source_p->localClient->last = CurrentTime;
+                       source_p->localClient->last = rb_current_time();
        }
 
-       if(chptr->mode.mode & MODE_NOCOLOR)
+       if(chptr->mode.mode & MODE_NOREPEAT)
        {
-               strlcpy(text2, text, BUFSIZE);
+               rb_strlcpy(text2, text, BUFSIZE);
+               strip_unprintable(text2);
+               md = channel_metadata_find(chptr, "NOREPEAT");
+               if(md && (!ConfigChannel.exempt_cmode_K || !is_any_op(msptr)))
+               {
+                       if(!(strcmp(md->value, text2)))
+                       {
+                               if(p_or_n != NOTICE)
+                                       sendto_one_numeric(source_p, 404, "%s :Cannot send to channel - Message blocked due to repeating (+K set)", chptr->chname);
+                               return;
+                       }
+               }
+               channel_metadata_delete(chptr, "NOREPEAT", 0);
+               channel_metadata_add(chptr, "NOREPEAT", text2, 0);
+       }
+
+       if(chptr->mode.mode & MODE_NOCOLOR && (!ConfigChannel.exempt_cmode_c || !is_any_op(msptr)))
+       {
+               rb_strlcpy(text2, text, BUFSIZE);
                strip_colour(text2);
                text = text2;
                if (EmptyString(text))
@@ -470,20 +536,109 @@ msg_channel(int p_or_n, const char *command,
                if(result == CAN_SEND_OPV ||
                   !flood_attack_channel(p_or_n, source_p, chptr, chptr->chname))
                {
+                       if (strlen(text) > 10 && chptr->mode.mode & MODE_NOCAPS && (!ConfigChannel.exempt_cmode_G || !is_any_op(msptr)))
+                       {
+                               rb_strlcpy(text2, text, BUFSIZE);
+                               strip_unprintable(text2);
+                               for(contor=0; contor < strlen(text2); contor++)
+                               {
+                                       if(IsUpper(text2[contor]) && !isdigit(text2[contor]))
+                                               caps++; 
+                                       len++;
+                               }
+                               if(((caps*100)/(len)) >= 50)
+                               {
+                                       sendto_one_numeric(source_p, 404, "%s :Cannot send to channel - Your message contains mostly capital letters (+G set)", chptr->chname);
+                                       return;
+                               }
+                       }
+                       if (p_or_n != PRIVMSG && chptr->mode.mode & MODE_NONOTICE && (!ConfigChannel.exempt_cmode_T || !is_any_op(msptr)))
+                       {
+                               sendto_one_numeric(source_p, 404, "%s :Cannot send to channel - Notices are disallowed (+T set)", chptr->chname);
+                               return;
+                       }
+                       if (p_or_n != NOTICE && chptr->mode.mode & MODE_NOACTION &&
+                                       !strncasecmp(text + 1, "ACTION", 6) &&
+                                       (!ConfigChannel.exempt_cmode_D || !is_any_op(msptr)))
+                       {
+                               sendto_one_numeric(source_p, 404, "%s :Cannot send to channel - ACTIONs are disallowed (+D set)", chptr->chname);
+                               return;
+                       }
+                       if (p_or_n != NOTICE && *text == '\001' &&
+                                       strncasecmp(text + 1, "ACTION", 6))
+                       {
+                               if (chptr->mode.mode & MODE_NOCTCP && (!ConfigChannel.exempt_cmode_C || !is_any_op(msptr)))
+                               {
+                                       sendto_one_numeric(source_p, 404, "%s :Cannot send to channel - CTCPs to this channel are disallowed (+C set)", chptr->chname);
+                                       return;
+                               }
+                               else if (rb_dlink_list_length(&chptr->locmembers) > (unsigned)(GlobalSetOptions.floodcount / 2))
+                                       source_p->large_ctcp_sent = rb_current_time();
+                       }
                        sendto_channel_flags(client_p, ALL_MEMBERS, source_p, chptr,
                                             "%s %s :%s", command, chptr->chname, text);
                }
        }
        else if(chptr->mode.mode & MODE_OPMODERATE &&
-                       chptr->mode.mode & MODE_MODERATED &&
-                       IsMember(source_p, chptr))
+                       (!(chptr->mode.mode & MODE_NOPRIVMSGS) ||
+                        IsMember(source_p, chptr)))
        {
-               /* only do +z for +m channels for now, as bans/quiets
-                * aren't tested for remote clients -- jilles */
                if(!flood_attack_channel(p_or_n, source_p, chptr, chptr->chname))
                {
-                       sendto_channel_flags(client_p, ONLY_CHANOPS, source_p, chptr,
-                                            "%s %s :%s", command, chptr->chname, text);
+                       sendto_channel_opmod(client_p, source_p, chptr,
+                                            command, text);
+               }
+       }
+       else
+       {
+               if(p_or_n != NOTICE)
+                       sendto_one_numeric(source_p, ERR_CANNOTSENDTOCHAN,
+                                          form_str(ERR_CANNOTSENDTOCHAN), chptr->chname);
+       }
+}
+/*
+ * msg_channel_opmod
+ *
+ * inputs      - flag privmsg or notice
+ *             - pointer to command "PRIVMSG" or "NOTICE"
+ *             - pointer to client_p
+ *             - pointer to source_p
+ *             - pointer to channel
+ * output      - NONE
+ * side effects        - message given channel ops
+ *
+ * XXX - We need to rework this a bit, it's a tad ugly. --nenolod
+ */
+static void
+msg_channel_opmod(int p_or_n, const char *command,
+                 struct Client *client_p, struct Client *source_p,
+                 struct Channel *chptr, const char *text)
+{
+       char text2[BUFSIZE];
+
+       if(chptr->mode.mode & MODE_NOCOLOR)
+       {
+               rb_strlcpy(text2, text, BUFSIZE);
+               strip_colour(text2);
+               text = text2;
+               if (EmptyString(text))
+               {
+                       /* could be empty after colour stripping and
+                        * that would cause problems later */
+                       if(p_or_n != NOTICE)
+                               sendto_one(source_p, form_str(ERR_NOTEXTTOSEND), me.name, source_p->name);
+                       return;
+               }
+       }
+
+       if(chptr->mode.mode & MODE_OPMODERATE &&
+                       (!(chptr->mode.mode & MODE_NOPRIVMSGS) ||
+                        IsMember(source_p, chptr)))
+       {
+               if(!flood_attack_channel(p_or_n, source_p, chptr, chptr->chname))
+               {
+                       sendto_channel_opmod(client_p, source_p, chptr,
+                                            command, text);
                }
        }
        else
@@ -530,102 +685,31 @@ msg_channel_flags(int p_or_n, const char *command, struct Client *client_p,
        {
                /* idletime shouldnt be reset by notice --fl */
                if(p_or_n != NOTICE)
-                       source_p->localClient->last = CurrentTime;
+                       source_p->localClient->last = rb_current_time();
        }
 
        sendto_channel_flags(client_p, type, source_p, chptr, "%s %c%s :%s",
                             command, c, chptr->chname, text);
 }
 
-#define PREV_FREE_TARGET(x) ((FREE_TARGET(x) == 0) ? 9 : FREE_TARGET(x) - 1)
-#define PREV_TARGET(i) ((i == 0) ? i = 9 : --i)
-#define NEXT_TARGET(i) ((i == 9) ? i = 0 : ++i)
-
 static void
 expire_tgchange(void *unused)
 {
        tgchange *target;
-       dlink_node *ptr, *next_ptr;
+       rb_dlink_node *ptr, *next_ptr;
 
-       DLINK_FOREACH_SAFE(ptr, next_ptr, tgchange_list.head)
+       RB_DLINK_FOREACH_SAFE(ptr, next_ptr, tgchange_list.head)
        {
                target = ptr->data;
 
-               if(target->expiry < CurrentTime)
-               {
-                       dlinkDelete(ptr, &tgchange_list);
-                       patricia_remove(tgchange_tree, target->pnode);
-                       MyFree(target->ip);
-                       MyFree(target);
-               }
-       }
-}
-
-static int
-add_target(struct Client *source_p, struct Client *target_p)
-{
-       int i, j;
-
-       /* can msg themselves or services without using any target slots */
-       if(source_p == target_p || IsService(target_p))
-               return 1;
-
-       /* special condition for those who have had PRIVMSG crippled to allow them
-        * to talk to IRCops still.
-        *
-        * XXX: is this controversial?
-        */
-       if(source_p->localClient->target_last > CurrentTime && IsOper(target_p))
-               return 1;
-
-       if(USED_TARGETS(source_p))
-       {
-               /* hunt for an existing target */
-               for(i = PREV_FREE_TARGET(source_p), j = USED_TARGETS(source_p);
-                   j; --j, PREV_TARGET(i))
+               if(target->expiry < rb_current_time())
                {
-                       if(source_p->localClient->targets[i] == target_p)
-                               return 1;
-               }
-
-               /* first message after connect, we may only start clearing
-                * slots after this message --anfl
-                */
-               if(!IsTGChange(source_p))
-               {
-                       SetTGChange(source_p);
-                       source_p->localClient->target_last = CurrentTime;
-               }
-               /* clear as many targets as we can */
-               else if((i = (CurrentTime - source_p->localClient->target_last) / 60))
-               {
-                       if(i > USED_TARGETS(source_p))
-                               USED_TARGETS(source_p) = 0;
-                       else
-                               USED_TARGETS(source_p) -= i;
-
-                       source_p->localClient->target_last = CurrentTime;
-               }
-               /* cant clear any, full target list */
-               else if(USED_TARGETS(source_p) == 10)
-               {
-                       add_tgchange(source_p->sockhost);
-                       return 0;
+                       rb_dlinkDelete(ptr, &tgchange_list);
+                       rb_patricia_remove(tgchange_tree, target->pnode);
+                       rb_free(target->ip);
+                       rb_free(target);
                }
        }
-       /* no targets in use, reset their target_last so that they cant
-        * abuse a long idle to get targets back more quickly
-        */
-       else
-       {
-               source_p->localClient->target_last = CurrentTime;
-               SetTGChange(source_p);
-       }
-
-       source_p->localClient->targets[FREE_TARGET(source_p)] = target_p;
-       NEXT_TARGET(FREE_TARGET(source_p));
-       ++USED_TARGETS(source_p);
-       return 1;
 }
 
 /*
@@ -644,19 +728,29 @@ static void
 msg_client(int p_or_n, const char *command,
           struct Client *source_p, struct Client *target_p, const char *text)
 {
+       int do_floodcount = 0;
+       struct Metadata *md;
+       struct DictionaryIter iter;
+       int oaccept = 0;
+       char text3[10];
+
        if(MyClient(source_p))
        {
                /* reset idle time for message only if its not to self 
                 * and its not a notice */
                if(p_or_n != NOTICE)
-                       source_p->localClient->last = CurrentTime;
+                       source_p->localClient->last = rb_current_time();
+
+               /* auto cprivmsg/cnotice */
+               do_floodcount = !IsOper(source_p) &&
+                       !find_allowing_channel(source_p, target_p);
 
                /* target change stuff, dont limit ctcp replies as that
                 * would allow people to start filling up random users
                 * targets just by ctcping them
                 */
                if((p_or_n != NOTICE || *text != '\001') &&
-                  ConfigFileEntry.target_change && !IsOper(source_p))
+                  ConfigFileEntry.target_change && do_floodcount)
                {
                        if(!add_target(source_p, target_p))
                        {
@@ -665,6 +759,14 @@ msg_client(int p_or_n, const char *command,
                                return;
                        }
                }
+
+               if (do_floodcount && p_or_n == NOTICE && *text == '\001' &&
+                               target_p->large_ctcp_sent + LARGE_CTCP_TIME >= rb_current_time())
+                       do_floodcount = 0;
+
+               if (do_floodcount &&
+                               flood_attack_client(p_or_n, source_p, target_p))
+                       return;
        }
        else if(source_p->from == target_p->from)
        {
@@ -680,13 +782,33 @@ msg_client(int p_or_n, const char *command,
 
        if(MyClient(target_p))
        {
-               /* XXX Controversial? allow opers always to send through a +g */
-               if(!IsServer(source_p) && (IsSetCallerId(target_p) ||
+               if (IsSetNoCTCP(target_p) && p_or_n != NOTICE && *text == '\001' && strncasecmp(text + 1, "ACTION", 6))
+               {   
+                           sendto_one_numeric(source_p, ERR_NOCTCP,
+                                   form_str(ERR_NOCTCP),
+                                   target_p->name);
+               }
+               /* If opers want to go through +g, they should load oaccept.*/
+               else if(!IsServer(source_p) && !IsService(source_p) && (IsSetCallerId(target_p) ||
+                                       (IsSetSCallerId(target_p) && !has_common_channel(source_p, target_p)) ||
                                        (IsSetRegOnlyMsg(target_p) && !source_p->user->suser[0])))
                {
+                       if (IsOper(source_p))
+                       {
+                               rb_snprintf(text3, sizeof(text3), "O%s", source_p->id);
+                               DICTIONARY_FOREACH(md, &iter, target_p->user->metadata)
+                               {
+                                       if(!strcmp(md->value, "OACCEPT") && !strcmp(md->name, text3))
+                                       {
+                                               oaccept = 1;
+                                               break;
+                                       }
+                               }
+                       }
                        /* Here is the anti-flood bot/spambot code -db */
-                       if(accept_message(source_p, target_p) || IsOper(source_p))
+                       if(accept_message(source_p, target_p) || oaccept)
                        {
+                               add_reply_target(target_p, source_p);
                                sendto_one(target_p, ":%s!%s@%s %s %s :%s",
                                           source_p->name,
                                           source_p->username,
@@ -698,8 +820,13 @@ msg_client(int p_or_n, const char *command,
                                        sendto_one_numeric(source_p, ERR_NONONREG,
                                                        form_str(ERR_NONONREG),
                                                        target_p->name);
-                               /* Only so opers can watch for floods */
-                               (void) flood_attack_client(p_or_n, source_p, target_p);
+                       }
+                       else if (IsSetSCallerId(target_p) && !has_common_channel(source_p, target_p))
+                       {
+                               if (p_or_n != NOTICE)
+                                       sendto_one_numeric(source_p, ERR_NOCOMMONCHAN,
+                                                       form_str(ERR_NOCOMMONCHAN),
+                                                       target_p->name);
                        }
                        else
                        {
@@ -712,37 +839,29 @@ msg_client(int p_or_n, const char *command,
                                }
 
                                if((target_p->localClient->last_caller_id_time +
-                                   ConfigFileEntry.caller_id_wait) < CurrentTime)
+                                   ConfigFileEntry.caller_id_wait) < rb_current_time())
                                {
                                        if(p_or_n != NOTICE)
                                                sendto_one_numeric(source_p, RPL_TARGNOTIFY,
                                                                   form_str(RPL_TARGNOTIFY),
                                                                   target_p->name);
 
+                                       add_reply_target(target_p, source_p);
                                        sendto_one(target_p, form_str(RPL_UMODEGMSG),
                                                   me.name, target_p->name, source_p->name,
                                                   source_p->username, source_p->host);
 
-                                       target_p->localClient->last_caller_id_time = CurrentTime;
+                                       target_p->localClient->last_caller_id_time = rb_current_time();
                                }
-                               /* Only so opers can watch for floods */
-                               (void) flood_attack_client(p_or_n, source_p, target_p);
                        }
                }
                else
                {
-                       /* If the client is remote, we dont perform a special check for
-                        * flooding.. as we wouldnt block their message anyway.. this means
-                        * we dont give warnings.. we then check if theyre opered 
-                        * (to avoid flood warnings), lastly if theyre our client
-                        * and flooding    -- fl */
-                       if(!MyClient(source_p) || IsOper(source_p) ||
-                          !flood_attack_client(p_or_n, source_p, target_p))
-                               sendto_anywhere(target_p, source_p, command, ":%s", text);
+                       add_reply_target(target_p, source_p);
+                       sendto_anywhere(target_p, source_p, command, ":%s", text);
                }
        }
-       else if(!MyClient(source_p) || IsOper(source_p) ||
-               !flood_attack_client(p_or_n, source_p, target_p))
+       else
                sendto_anywhere(target_p, source_p, command, ":%s", text);
 
        return;
@@ -762,33 +881,38 @@ flood_attack_client(int p_or_n, struct Client *source_p, struct Client *target_p
 {
        int delta;
 
-       if(GlobalSetOptions.floodcount && MyConnect(target_p) && IsClient(source_p))
+       /* Services could get many messages legitimately and
+        * can be messaged without rate limiting via aliases
+        * and msg user@server.
+        * -- jilles
+        */
+       if(GlobalSetOptions.floodcount && IsClient(source_p) && source_p != target_p && !IsService(target_p) && (!IsOper(source_p) || !ConfigFileEntry.true_no_oper_flood))
        {
-               if((target_p->localClient->first_received_message_time + 1) < CurrentTime)
+               if((target_p->first_received_message_time + 1) < rb_current_time())
                {
-                       delta = CurrentTime - target_p->localClient->first_received_message_time;
-                       target_p->localClient->received_number_of_privmsgs -= delta;
-                       target_p->localClient->first_received_message_time = CurrentTime;
-                       if(target_p->localClient->received_number_of_privmsgs <= 0)
+                       delta = rb_current_time() - target_p->first_received_message_time;
+                       target_p->received_number_of_privmsgs -= delta;
+                       target_p->first_received_message_time = rb_current_time();
+                       if(target_p->received_number_of_privmsgs <= 0)
                        {
-                               target_p->localClient->received_number_of_privmsgs = 0;
-                               target_p->localClient->flood_noticed = 0;
+                               target_p->received_number_of_privmsgs = 0;
+                               target_p->flood_noticed = 0;
                        }
                }
 
-               if((target_p->localClient->received_number_of_privmsgs >=
-                   GlobalSetOptions.floodcount) || target_p->localClient->flood_noticed)
+               if((target_p->received_number_of_privmsgs >=
+                   GlobalSetOptions.floodcount) || target_p->flood_noticed)
                {
-                       if(target_p->localClient->flood_noticed == 0)
+                       if(target_p->flood_noticed == 0)
                        {
-                               sendto_realops_snomask(SNO_BOTS, L_ALL,
+                               sendto_realops_snomask(SNO_BOTS, L_NETWIDE,
                                                     "Possible Flooder %s[%s@%s] on %s target: %s",
                                                     source_p->name, source_p->username,
                                                     source_p->orighost,
                                                     source_p->servptr->name, target_p->name);
-                               target_p->localClient->flood_noticed = 1;
+                               target_p->flood_noticed = 1;
                                /* add a bit of penalty */
-                               target_p->localClient->received_number_of_privmsgs += 2;
+                               target_p->received_number_of_privmsgs += 2;
                        }
                        if(MyClient(source_p) && (p_or_n != NOTICE))
                                sendto_one(source_p,
@@ -797,7 +921,7 @@ flood_attack_client(int p_or_n, struct Client *source_p, struct Client *target_p
                        return 1;
                }
                else
-                       target_p->localClient->received_number_of_privmsgs++;
+                       target_p->received_number_of_privmsgs++;
        }
 
        return 0;
@@ -817,13 +941,13 @@ flood_attack_channel(int p_or_n, struct Client *source_p, struct Channel *chptr,
 {
        int delta;
 
-       if(GlobalSetOptions.floodcount && MyClient(source_p))
+       if(GlobalSetOptions.floodcount && MyClient(source_p) && (!IsOper(source_p) || !ConfigFileEntry.true_no_oper_flood))
        {
-               if((chptr->first_received_message_time + 1) < CurrentTime)
+               if((chptr->first_received_message_time + 1) < rb_current_time())
                {
-                       delta = CurrentTime - chptr->first_received_message_time;
+                       delta = rb_current_time() - chptr->first_received_message_time;
                        chptr->received_number_of_privmsgs -= delta;
-                       chptr->first_received_message_time = CurrentTime;
+                       chptr->first_received_message_time = rb_current_time();
                        if(chptr->received_number_of_privmsgs <= 0)
                        {
                                chptr->received_number_of_privmsgs = 0;
@@ -836,7 +960,7 @@ flood_attack_channel(int p_or_n, struct Client *source_p, struct Channel *chptr,
                {
                        if(chptr->flood_noticed == 0)
                        {
-                               sendto_realops_snomask(SNO_BOTS, L_ALL,
+                               sendto_realops_snomask(SNO_BOTS, *chptr->chname == '&' ? L_ALL : L_NETWIDE,
                                                     "Possible Flooder %s[%s@%s] on %s target: %s",
                                                     source_p->name, source_p->username,
                                                     source_p->orighost,
@@ -881,10 +1005,8 @@ handle_special(int p_or_n, const char *command, struct Client *client_p,
               struct Client *source_p, const char *nick, const char *text)
 {
        struct Client *target_p;
-       char *host;
        char *server;
        char *s;
-       int count;
 
        /* user[%host]@server addressed?
         * NOTE: users can send to user@server, but not user%host@server
@@ -899,8 +1021,6 @@ handle_special(int p_or_n, const char *command, struct Client *client_p,
                        return;
                }
 
-               count = 0;
-
                if(!IsOper(source_p))
                {
                        if(strchr(nick, '%') || (strncmp(nick, "opers", 5) == 0))
@@ -919,39 +1039,23 @@ handle_special(int p_or_n, const char *command, struct Client *client_p,
                        return;
                }
 
-               *server = '\0';
-
-               if((host = strchr(nick, '%')) != NULL)
-                       *host++ = '\0';
-
                /* Check if someones msg'ing opers@our.server */
-               if(strcmp(nick, "opers") == 0)
+               if(strncmp(nick, "opers@", 6) == 0)
                {
                        sendto_realops_snomask(SNO_GENERAL, L_ALL, "To opers: From: %s: %s",
                                             source_p->name, text);
                        return;
                }
 
-               /*
-                * Look for users which match the destination host
-                * (no host == wildcard) and if one and one only is
-                * found connected to me, deliver message!
+               /* This was not very useful except for bypassing certain
+                * restrictions. Note that we still allow sending to
+                * remote servers this way, for messaging pseudoservers
+                * securely whether they have a service{} block or not.
+                * -- jilles
                 */
-               target_p = find_userhost(nick, host, &count);
-
-               if(target_p != NULL)
-               {
-                       if(server != NULL)
-                               *server = '@';
-                       if(host != NULL)
-                               *--host = '%';
-
-                       if(count == 1)
-                               sendto_anywhere(target_p, source_p, command, ":%s", text);
-                       else
-                               sendto_one(source_p, form_str(ERR_TOOMANYTARGETS),
-                                          get_id(&me, source_p), get_id(source_p, source_p), nick);
-               }
+               sendto_one_numeric(source_p, ERR_NOSUCHNICK,
+                                  form_str(ERR_NOSUCHNICK), nick);
+               return;
        }
 
        /*
@@ -999,41 +1103,8 @@ handle_special(int p_or_n, const char *command, struct Client *client_p,
                                    nick + 1,
                                    (*nick == '#') ? MATCH_HOST : MATCH_SERVER,
                                    "%s $%s :%s", command, nick, text);
+               if (p_or_n != NOTICE && *text == '\001')
+                       source_p->large_ctcp_sent = rb_current_time();
                return;
        }
 }
-
-/*
- * find_userhost - find a user@host (server or user).
- * inputs       - user name to look for
- *              - host name to look for
- *             - pointer to count of number of matches found
- * outputs     - pointer to client if found
- *             - count is updated
- * side effects        - none
- *
- */
-static struct Client *
-find_userhost(const char *user, const char *host, int *count)
-{
-       struct Client *c2ptr;
-       struct Client *res = NULL;
-       char *u = LOCAL_COPY(user);
-       dlink_node *ptr;
-       *count = 0;
-       if(collapse(u) != NULL)
-       {
-               DLINK_FOREACH(ptr, global_client_list.head)
-               {
-                       c2ptr = ptr->data;
-                       if(!MyClient(c2ptr))    /* implies mine and an user */
-                               continue;
-                       if((!host || match(host, c2ptr->host)) && irccmp(u, c2ptr->username) == 0)
-                       {
-                               (*count)++;
-                               res = c2ptr;
-                       }
-               }
-       }
-       return (res);
-}