]> jfr.im git - solanum.git/blobdiff - src/channel.c
Add topic TS and channel TS constraints for /LIST.
[solanum.git] / src / channel.c
index 183a4e34fba42a724fb6da2cddd862dfadafb758..bb49f1d57c6868f74401e7d2ac1abee849b2f22f 100644 (file)
 
 #include "stdinc.h"
 #include "channel.h"
+#include "chmode.h"
 #include "client.h"
 #include "common.h"
 #include "hash.h"
 #include "hook.h"
-#include "irc_string.h"
-#include "sprintf_irc.h"
+#include "match.h"
 #include "ircd.h"
 #include "numeric.h"
 #include "s_serv.h"            /* captab */
 #include "whowas.h"
 #include "s_conf.h"            /* ConfigFileEntry, ConfigChannel */
 #include "s_newconf.h"
-#include "s_log.h"
+#include "logger.h"
 
-extern rb_dlink_list global_channel_list;
-
-extern struct config_channel_entry ConfigChannel;
-extern BlockHeap *channel_heap;
-extern BlockHeap *ban_heap;
-extern BlockHeap *topic_heap;
-extern BlockHeap *member_heap;
+struct config_channel_entry ConfigChannel;
+rb_dlink_list global_channel_list;
+static rb_bh *channel_heap;
+static rb_bh *ban_heap;
+static rb_bh *topic_heap;
+static rb_bh *member_heap;
 
 static int channel_capabs[] = { CAP_EX, CAP_IE,
        CAP_SERVICE,
@@ -63,6 +62,8 @@ static struct ChCapCombo chcap_combos[NCHCAP_COMBOS];
 static void free_topic(struct Channel *chptr);
 
 static int h_can_join;
+static int h_can_send;
+int h_get_channel_access;
 
 /* init_channels()
  *
@@ -73,12 +74,14 @@ static int h_can_join;
 void
 init_channels(void)
 {
-       channel_heap = BlockHeapCreate(sizeof(struct Channel), CHANNEL_HEAP_SIZE);
-       ban_heap = BlockHeapCreate(sizeof(struct Ban), BAN_HEAP_SIZE);
-       topic_heap = BlockHeapCreate(TOPICLEN + 1 + USERHOST_REPLYLEN, TOPIC_HEAP_SIZE);
-       member_heap = BlockHeapCreate(sizeof(struct membership), MEMBER_HEAP_SIZE);
+       channel_heap = rb_bh_create(sizeof(struct Channel), CHANNEL_HEAP_SIZE, "channel_heap");
+       ban_heap = rb_bh_create(sizeof(struct Ban), BAN_HEAP_SIZE, "ban_heap");
+       topic_heap = rb_bh_create(TOPICLEN + 1 + USERHOST_REPLYLEN, TOPIC_HEAP_SIZE, "topic_heap");
+       member_heap = rb_bh_create(sizeof(struct membership), MEMBER_HEAP_SIZE, "member_heap");
 
        h_can_join = register_hook("can_join");
+       h_can_send = register_hook("can_send");
+       h_get_channel_access = register_hook("get_channel_access");
 }
 
 /*
@@ -88,7 +91,7 @@ struct Channel *
 allocate_channel(const char *chname)
 {
        struct Channel *chptr;
-       chptr = BlockHeapAlloc(channel_heap);
+       chptr = rb_bh_alloc(channel_heap);
        chptr->chname = rb_strdup(chname);
        return (chptr);
 }
@@ -97,14 +100,15 @@ void
 free_channel(struct Channel *chptr)
 {
        rb_free(chptr->chname);
-       BlockHeapFree(channel_heap, chptr);
+       rb_free(chptr->mode_lock);
+       rb_bh_free(channel_heap, chptr);
 }
 
 struct Ban *
 allocate_ban(const char *banstr, const char *who)
 {
        struct Ban *bptr;
-       bptr = BlockHeapAlloc(ban_heap);
+       bptr = rb_bh_alloc(ban_heap);
        bptr->banstr = rb_strdup(banstr);
        bptr->who = rb_strdup(who);
 
@@ -116,9 +120,30 @@ free_ban(struct Ban *bptr)
 {
        rb_free(bptr->banstr);
        rb_free(bptr->who);
-       BlockHeapFree(ban_heap, bptr);
+       rb_bh_free(ban_heap, bptr);
 }
 
+/*
+ * send_channel_join()
+ *
+ * input        - channel to join, client joining.
+ * output       - none
+ * side effects - none
+ */
+void
+send_channel_join(struct Channel *chptr, struct Client *client_p)
+{
+       if (!IsClient(client_p))
+               return;
+
+       sendto_channel_local_with_capability(ALL_MEMBERS, NOCAPS, CLICAP_EXTENDED_JOIN, chptr, ":%s!%s@%s JOIN %s",
+                                            client_p->name, client_p->username, client_p->host, chptr->chname);
+
+       sendto_channel_local_with_capability(ALL_MEMBERS, CLICAP_EXTENDED_JOIN, NOCAPS, chptr, ":%s!%s@%s JOIN %s %s %ld :%s",
+                                            client_p->name, client_p->username, client_p->host, chptr->chname,
+                                            EmptyString(client_p->user->suser) ? "*" : client_p->user->suser,
+                                            client_p->tsinfo, client_p->info);
+}
 
 /* find_channel_membership()
  *
@@ -205,7 +230,7 @@ add_user_to_channel(struct Channel *chptr, struct Client *client_p, int flags)
        if(client_p->user == NULL)
                return;
 
-       msptr = BlockHeapAlloc(member_heap);
+       msptr = rb_bh_alloc(member_heap);
 
        msptr->chptr = chptr;
        msptr->client_p = client_p;
@@ -242,12 +267,10 @@ remove_user_from_channel(struct membership *msptr)
        if(client_p->servptr == &me)
                rb_dlinkDelete(&msptr->locchannode, &chptr->locmembers);
 
-       chptr->users_last = rb_current_time();
-
        if(!(chptr->mode.mode & MODE_PERMANENT) && rb_dlink_list_length(&chptr->members) <= 0)
                destroy_channel(chptr);
 
-       BlockHeapFree(member_heap, msptr);
+       rb_bh_free(member_heap, msptr);
 
        return;
 }
@@ -279,12 +302,10 @@ remove_user_from_channels(struct Client *client_p)
                if(client_p->servptr == &me)
                        rb_dlinkDelete(&msptr->locchannode, &chptr->locmembers);
 
-               chptr->users_last = rb_current_time();
-
                if(!(chptr->mode.mode & MODE_PERMANENT) && rb_dlink_list_length(&chptr->members) <= 0)
                        destroy_channel(chptr);
 
-               BlockHeapFree(member_heap, msptr);
+               rb_bh_free(member_heap, msptr);
        }
 
        client_p->user->channel.head = client_p->user->channel.tail = NULL;
@@ -709,6 +730,7 @@ is_quieted(struct Channel *chptr, struct Client *who, struct membership *msptr,
  * input       - client to check, channel to check for, key
  * output      - reason for not being able to join, else 0
  * side effects -
+ * caveats      - this function should only be called on a local user.
  */
 int
 can_join(struct Client *source_p, struct Channel *chptr, char *key)
@@ -725,6 +747,10 @@ can_join(struct Client *source_p, struct Channel *chptr, char *key)
 
        s_assert(source_p->localClient != NULL);
 
+       moduledata.client = source_p;
+       moduledata.chptr = chptr;
+       moduledata.approved = 0;
+
        rb_sprintf(src_host, "%s!%s@%s", source_p->name, source_p->username, source_p->host);
        rb_sprintf(src_iphost, "%s!%s@%s", source_p->name, source_p->username, source_p->sockhost);
        if(source_p->localClient->mangledhost != NULL)
@@ -745,7 +771,10 @@ can_join(struct Client *source_p, struct Channel *chptr, char *key)
        }
 
        if((is_banned(chptr, source_p, NULL, src_host, src_iphost)) == CHFL_BAN)
-               return (ERR_BANNEDFROMCHAN);
+       {
+               moduledata.approved = ERR_BANNEDFROMCHAN;
+               goto finish_join_check;
+       }
 
        if(chptr->mode.mode & MODE_INVITEONLY)
        {
@@ -757,7 +786,7 @@ can_join(struct Client *source_p, struct Channel *chptr, char *key)
                if(invite == NULL)
                {
                        if(!ConfigChannel.use_invex)
-                               return (ERR_INVITEONLYCHAN);
+                               moduledata.approved = ERR_INVITEONLYCHAN;
                        RB_DLINK_FOREACH(ptr, chptr->invexlist.head)
                        {
                                invex = ptr->data;
@@ -769,12 +798,12 @@ can_join(struct Client *source_p, struct Channel *chptr, char *key)
                                        break;
                        }
                        if(ptr == NULL)
-                               return (ERR_INVITEONLYCHAN);
+                               moduledata.approved = ERR_INVITEONLYCHAN;
                }
        }
 
        if(*chptr->mode.key && (EmptyString(key) || irccmp(chptr->mode.key, key)))
-               return (ERR_BADCHANNELKEY);
+               moduledata.approved = ERR_BADCHANNELKEY;
 
        if(chptr->mode.limit &&
           rb_dlink_list_length(&chptr->members) >= (unsigned long) chptr->mode.limit)
@@ -799,13 +828,10 @@ can_join(struct Client *source_p, struct Channel *chptr, char *key)
                                break;
                }
                if (invite == NULL)
-                       return i;
+                       moduledata.approved = i;
        }
 
-       moduledata.client = source_p;
-       moduledata.chptr = chptr;
-       moduledata.approved = 0;
-
+finish_join_check:
        call_hook(h_can_join, &moduledata);
 
        return moduledata.approved;
@@ -820,12 +846,16 @@ can_join(struct Client *source_p, struct Channel *chptr, char *key)
 int
 can_send(struct Channel *chptr, struct Client *source_p, struct membership *msptr)
 {
+       hook_data_channel_approval moduledata;
+
+       moduledata.approved = CAN_SEND_NONOP;
+
        if(IsServer(source_p) || IsService(source_p))
                return CAN_SEND_OPV;
 
        if(MyClient(source_p) && hash_find_resv(chptr->chname) &&
           !IsOper(source_p) && !IsExemptResv(source_p))
-               return CAN_SEND_NO;
+               moduledata.approved = CAN_SEND_NO;
 
        if(msptr == NULL)
        {
@@ -838,17 +868,16 @@ can_send(struct Channel *chptr, struct Client *source_p, struct membership *mspt
                         * theres no possibility of caching them --fl
                         */
                        if(chptr->mode.mode & MODE_NOPRIVMSGS || chptr->mode.mode & MODE_MODERATED)
-                               return CAN_SEND_NO;
+                               moduledata.approved = CAN_SEND_NO;
                        else
-                               return CAN_SEND_NONOP;
+                               moduledata.approved = CAN_SEND_NONOP;
+
+                       return moduledata.approved;
                }
        }
 
-       if(is_chanop_voiced(msptr))
-               return CAN_SEND_OPV;
-
        if(chptr->mode.mode & MODE_MODERATED)
-               return CAN_SEND_NO;
+               moduledata.approved = CAN_SEND_NO;
 
        if(MyClient(source_p))
        {
@@ -856,14 +885,75 @@ can_send(struct Channel *chptr, struct Client *source_p, struct membership *mspt
                if(msptr->bants == chptr->bants)
                {
                        if(can_send_banned(msptr))
-                               return CAN_SEND_NO;
+                               moduledata.approved = CAN_SEND_NO;
                }
                else if(is_banned(chptr, source_p, msptr, NULL, NULL) == CHFL_BAN
                        || is_quieted(chptr, source_p, msptr, NULL, NULL) == CHFL_BAN)
-                       return CAN_SEND_NO;
+                       moduledata.approved = CAN_SEND_NO;
+       }
+
+       if(is_chanop_voiced(msptr))
+               moduledata.approved = CAN_SEND_OPV;
+
+       call_hook(h_can_send, &moduledata);
+
+       return moduledata.approved;
+}
+
+/*
+ * flood_attack_channel
+ * inputs       - flag 0 if PRIVMSG 1 if NOTICE. RFC
+ *                says NOTICE must not auto reply
+ *              - pointer to source Client 
+ *             - pointer to target channel
+ * output      - 1 if target is under flood attack
+ * side effects        - check for flood attack on target chptr
+ */
+int
+flood_attack_channel(int p_or_n, struct Client *source_p, struct Channel *chptr, char *chname)
+{
+       int delta;
+
+       if(GlobalSetOptions.floodcount && MyClient(source_p))
+       {
+               if((chptr->first_received_message_time + 1) < rb_current_time())
+               {
+                       delta = rb_current_time() - chptr->first_received_message_time;
+                       chptr->received_number_of_privmsgs -= delta;
+                       chptr->first_received_message_time = rb_current_time();
+                       if(chptr->received_number_of_privmsgs <= 0)
+                       {
+                               chptr->received_number_of_privmsgs = 0;
+                               chptr->flood_noticed = 0;
+                       }
+               }
+
+               if((chptr->received_number_of_privmsgs >= GlobalSetOptions.floodcount)
+                  || chptr->flood_noticed)
+               {
+                       if(chptr->flood_noticed == 0)
+                       {
+                               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,
+                                                    source_p->servptr->name, chptr->chname);
+                               chptr->flood_noticed = 1;
+
+                               /* Add a bit of penalty */
+                               chptr->received_number_of_privmsgs += 2;
+                       }
+                       if(MyClient(source_p) && (p_or_n != 1))
+                               sendto_one(source_p,
+                                          ":%s NOTICE %s :*** Message to %s throttled due to flooding",
+                                          me.name, source_p->name, chptr->chname);
+                       return 1;
+               }
+               else
+                       chptr->received_number_of_privmsgs++;
        }
 
-       return CAN_SEND_NONOP;
+       return 0;
 }
 
 /* find_bannickchange_channel()
@@ -923,19 +1013,14 @@ check_spambot_warning(struct Client *source_p, const char *name)
                        source_p->localClient->oper_warn_count_down--;
                else
                        source_p->localClient->oper_warn_count_down = 0;
-               if(source_p->localClient->oper_warn_count_down == 0)
+               if(source_p->localClient->oper_warn_count_down == 0 &&
+                               name != NULL)
                {
                        /* Its already known as a possible spambot */
-                       if(name != NULL)
-                               sendto_realops_snomask(SNO_BOTS, L_NETWIDE,
-                                                    "User %s (%s@%s) trying to join %s is a possible spambot",
-                                                    source_p->name,
-                                                    source_p->username, source_p->orighost, name);
-                       else
-                               sendto_realops_snomask(SNO_BOTS, L_NETWIDE,
-                                                    "User %s (%s@%s) is a possible spambot",
-                                                    source_p->name,
-                                                    source_p->username, source_p->orighost);
+                       sendto_realops_snomask(SNO_BOTS, L_NETWIDE,
+                                            "User %s (%s@%s) trying to join %s is a possible spambot",
+                                            source_p->name,
+                                            source_p->username, source_p->orighost, name);
                        source_p->localClient->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
                }
        }
@@ -946,7 +1031,9 @@ check_spambot_warning(struct Client *source_p, const char *name)
                   JOIN_LEAVE_COUNT_EXPIRE_TIME)
                {
                        decrement_count = (t_delta / JOIN_LEAVE_COUNT_EXPIRE_TIME);
-                       if(decrement_count > source_p->localClient->join_leave_count)
+                       if(name != NULL)
+                               ;
+                       else if(decrement_count > source_p->localClient->join_leave_count)
                                source_p->localClient->join_leave_count = 0;
                        else
                                source_p->localClient->join_leave_count -= decrement_count;
@@ -989,7 +1076,7 @@ check_splitmode(void *unused)
                                splitmode = 1;
                                sendto_realops_snomask(SNO_GENERAL, L_ALL,
                                                     "Network split, activating splitmode");
-                               eventAddIsh("check_splitmode", check_splitmode, NULL, 2);
+                               check_splitmode_ev = rb_event_addish("check_splitmode", check_splitmode, NULL, 2);
                        }
                }
                /* in splitmode, check whether its finished */
@@ -1000,7 +1087,8 @@ check_splitmode(void *unused)
                        sendto_realops_snomask(SNO_GENERAL, L_ALL,
                                             "Network rejoined, deactivating splitmode");
 
-                       eventDelete(check_splitmode, NULL);
+                       rb_event_delete(check_splitmode_ev);
+                       check_splitmode_ev = NULL;
                }
        }
 }
@@ -1020,7 +1108,7 @@ allocate_topic(struct Channel *chptr)
        if(chptr == NULL)
                return;
 
-       ptr = BlockHeapAlloc(topic_heap);
+       ptr = rb_bh_alloc(topic_heap);
 
        /* Basically we allocate one large block for the topic and
         * the topic info.  We then split it up into two and shove it
@@ -1050,7 +1138,7 @@ free_topic(struct Channel *chptr)
         * MUST change this as well
         */
        ptr = chptr->topic;
-       BlockHeapFree(topic_heap, ptr);
+       rb_bh_free(topic_heap, ptr);
        chptr->topic = NULL;
        chptr->topic_info = NULL;
 }
@@ -1068,8 +1156,8 @@ set_channel_topic(struct Channel *chptr, const char *topic, const char *topic_in
        {
                if(chptr->topic == NULL)
                        allocate_topic(chptr);
-               strlcpy(chptr->topic, topic, TOPICLEN + 1);
-               strlcpy(chptr->topic_info, topic_info, USERHOST_REPLYLEN);
+               rb_strlcpy(chptr->topic, topic, TOPICLEN + 1);
+               rb_strlcpy(chptr->topic_info, topic_info, USERHOST_REPLYLEN);
                chptr->topic_time = topicts;
        }
        else
@@ -1080,25 +1168,6 @@ set_channel_topic(struct Channel *chptr, const char *topic, const char *topic_in
        }
 }
 
-const struct mode_letter chmode_flags[] =
-{
-       {MODE_INVITEONLY, 'i'},
-       {MODE_MODERATED, 'm'},
-       {MODE_NOPRIVMSGS, 'n'},
-       {MODE_PRIVATE, 'p'},
-       {MODE_SECRET, 's'},
-       {MODE_TOPICLIMIT, 't'},
-       {MODE_NOCOLOR, 'c'},
-       {MODE_FREEINVITE, 'g'},
-       {MODE_OPMODERATE, 'z'},
-       {MODE_EXLIMIT, 'L'},
-       {MODE_PERMANENT, 'P'},
-       {MODE_FREETARGET, 'F'},
-       {MODE_DISFORWARD, 'Q'},
-       {MODE_REGONLY, 'r'},
-       {0, '\0'}
-};
-
 /* channel_modes()
  *
  * inputs       - pointer to channel
@@ -1121,9 +1190,9 @@ channel_modes(struct Channel *chptr, struct Client *client_p)
        *mbuf++ = '+';
        *pbuf = '\0';
 
-       for (i = 0; chmode_flags[i].mode; ++i)
-               if(chptr->mode.mode & chmode_flags[i].mode)
-                       *mbuf++ = chmode_flags[i].letter;
+       for (i = 0; i < 256; i++)
+               if(chptr->mode.mode & chmode_flags[i])
+                       *mbuf++ = i;
 
        if(chptr->mode.limit)
        {
@@ -1160,8 +1229,8 @@ channel_modes(struct Channel *chptr, struct Client *client_p)
 
        *mbuf = '\0';
 
-       strlcpy(final, buf1, sizeof final);
-       strlcat(final, buf2, sizeof final);
+       rb_strlcpy(final, buf1, sizeof final);
+       rb_strlcat(final, buf2, sizeof final);
        return final;
 }
 
@@ -1304,13 +1373,9 @@ send_cap_mode_changes(struct Client *client_p, struct Client *source_p,
                cap = chcap_combos[j].cap_yes;
                nocap = chcap_combos[j].cap_no;
 
-               if(cap & CAP_TS6)
-                       mbl = preflen = rb_sprintf(modebuf, ":%s TMODE %ld %s ",
-                                                  use_id(source_p), (long) chptr->channelts,
-                                                  chptr->chname);
-               else
-                       mbl = preflen = rb_sprintf(modebuf, ":%s MODE %s ",
-                                                  source_p->name, chptr->chname);
+               mbl = preflen = rb_sprintf(modebuf, ":%s TMODE %ld %s ",
+                                          use_id(source_p), (long) chptr->channelts,
+                                          chptr->chname);
 
                /* loop the list of - modes we have */
                for (i = 0; i < mode_count; i++)
@@ -1324,7 +1389,7 @@ send_cap_mode_changes(struct Client *client_p, struct Client *source_p,
                           || ((nocap & mode_changes[i].nocaps) != mode_changes[i].nocaps))
                                continue;
 
-                       if((cap & CAP_TS6) && !EmptyString(mode_changes[i].id))
+                       if(!EmptyString(mode_changes[i].id))
                                arg = mode_changes[i].id;
                        else
                                arg = mode_changes[i].arg;
@@ -1387,3 +1452,54 @@ send_cap_mode_changes(struct Client *client_p, struct Client *source_p,
                        sendto_server(client_p, chptr, cap, nocap, "%s %s", modebuf, parabuf);
        }
 }
+
+void 
+resv_chan_forcepart(const char *name, const char *reason, int temp_time)
+{
+       rb_dlink_node *ptr;
+       rb_dlink_node *next_ptr;
+       struct Channel *chptr;
+       struct membership *msptr;
+       struct Client *target_p;
+
+       if(!ConfigChannel.resv_forcepart)
+               return;
+
+       /* for each user on our server in the channel list
+        * send them a PART, and notify opers.
+        */
+       chptr = find_channel(name);
+       if(chptr != NULL)
+       {
+               RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->locmembers.head)
+               {
+                       msptr = ptr->data;
+                       target_p = msptr->client_p;
+
+                       if(IsExemptResv(target_p))
+                               continue;
+
+                       sendto_server(target_p, chptr, CAP_TS6, NOCAPS,
+                                     ":%s PART %s", target_p->id, chptr->chname);
+
+                       sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s PART %s :%s",
+                                            target_p->name, target_p->username,
+                                            target_p->host, chptr->chname, target_p->name);
+
+                       remove_user_from_channel(msptr);
+
+                       /* notify opers & user they were removed from the channel */
+                       sendto_realops_snomask(SNO_GENERAL, L_ALL,
+                                            "Forced PART for %s!%s@%s from %s (%s)",
+                                            target_p->name, target_p->username, 
+                                            target_p->host, name, reason);
+
+                       if(temp_time > 0)
+                               sendto_one_notice(target_p, ":*** Channel %s is temporarily unavailable on this server.",
+                                          name);
+                       else
+                               sendto_one_notice(target_p, ":*** Channel %s is no longer available on this server.",
+                                          name);
+               }
+       }
+}