ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/src/channel.c
Revision: 9416
Committed: Sat Jun 13 15:57:05 2020 UTC (3 years, 9 months ago) by michael
Content type: text/x-csrc
File size: 32206 byte(s)
Log Message:
- Remove hardcoded numeric digit from RPL_NAMREPLY

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2020 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file channel.c
23 * \brief Responsible for managing channels, members, bans and topics
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "channel.h"
30 #include "channel_invite.h"
31 #include "channel_mode.h"
32 #include "client.h"
33 #include "hash.h"
34 #include "conf.h"
35 #include "conf_resv.h"
36 #include "hostmask.h"
37 #include "irc_string.h"
38 #include "ircd.h"
39 #include "numeric.h"
40 #include "server.h"
41 #include "send.h"
42 #include "event.h"
43 #include "memory.h"
44 #include "misc.h"
45 #include "extban.h"
46
47
48 /** Doubly linked list containing a list of all channels. */
49 static dlink_list channel_list;
50
51
52 /*! \brief Returns the channel_list as constant
53 * \return channel_list
54 */
55 const dlink_list *
56 channel_get_list(void)
57 {
58 return &channel_list;
59 }
60
61 /*! \brief Adds a user to a channel by adding another link to the
62 * channels member chain.
63 * \param channel Pointer to channel to add client to
64 * \param client Pointer to client (who) to add
65 * \param flags Flags for chanops etc
66 * \param flood_ctrl Whether to count this join in flood calculations
67 */
68 void
69 add_user_to_channel(struct Channel *channel, struct Client *client,
70 unsigned int flags, bool flood_ctrl)
71 {
72 assert(IsClient(client));
73
74 if (GlobalSetOptions.joinfloodtime)
75 {
76 if (flood_ctrl == true)
77 ++channel->number_joined;
78
79 channel->number_joined -= (event_base->time.sec_monotonic - channel->last_join_time) *
80 (((float)GlobalSetOptions.joinfloodcount) /
81 (float)GlobalSetOptions.joinfloodtime);
82
83 if (channel->number_joined <= 0)
84 {
85 channel->number_joined = 0;
86 ClearJoinFloodNoticed(channel);
87 }
88 else if (channel->number_joined >= GlobalSetOptions.joinfloodcount)
89 {
90 channel->number_joined = GlobalSetOptions.joinfloodcount;
91
92 if (!IsSetJoinFloodNoticed(channel))
93 {
94 SetJoinFloodNoticed(channel);
95 sendto_realops_flags(UMODE_BOTS, L_ALL, SEND_NOTICE,
96 "Possible Join Flooder %s on %s target: %s",
97 client_get_name(client, HIDE_IP),
98 client->servptr->name, channel->name);
99 }
100 }
101
102 channel->last_join_time = event_base->time.sec_monotonic;
103 }
104
105 struct ChannelMember *member = xcalloc(sizeof(*member));
106 member->client = client;
107 member->channel = channel;
108 member->flags = flags;
109
110 dlinkAdd(member, &member->channode, &channel->members);
111
112 if (MyConnect(client))
113 dlinkAdd(member, &member->locchannode, &channel->members_local);
114
115 dlinkAdd(member, &member->usernode, &client->channel);
116 }
117
118 /*! \brief Deletes an user from a channel by removing a link in the
119 * channels member chain.
120 * \param member Pointer to Membership struct
121 */
122 void
123 remove_user_from_channel(struct ChannelMember *member)
124 {
125 struct Client *const client = member->client;
126 struct Channel *const channel = member->channel;
127
128 dlinkDelete(&member->channode, &channel->members);
129
130 if (MyConnect(client))
131 dlinkDelete(&member->locchannode, &channel->members_local);
132
133 dlinkDelete(&member->usernode, &client->channel);
134
135 xfree(member);
136
137 if (channel->members.head == NULL)
138 channel_free(channel);
139 }
140
141 /* channel_send_members()
142 *
143 * inputs -
144 * output - NONE
145 * side effects -
146 */
147 static void
148 channel_send_members(struct Client *client, const struct Channel *channel,
149 const char *modebuf, const char *parabuf)
150 {
151 dlink_node *node;
152 char buf[IRCD_BUFSIZE];
153 int tlen; /* length of text to append */
154 char *t, *start; /* temp char pointer */
155
156 start = t = buf + snprintf(buf, sizeof(buf), ":%s SJOIN %ju %s %s %s:",
157 me.id, channel->creation_time,
158 channel->name, modebuf, parabuf);
159
160 DLINK_FOREACH(node, channel->members.head)
161 {
162 const struct ChannelMember *member = node->data;
163
164 tlen = strlen(member->client->id) + 1; /* +1 for space */
165
166 if (member->flags & CHFL_CHANOP)
167 ++tlen;
168 if (member->flags & CHFL_HALFOP)
169 ++tlen;
170 if (member->flags & CHFL_VOICE)
171 ++tlen;
172
173 /*
174 * Space will be converted into CR, but we also need space for LF..
175 * That's why we use '- 1' here -adx
176 */
177 if (t + tlen - buf > IRCD_BUFSIZE - 1)
178 {
179 *(t - 1) = '\0'; /* Kill the space and terminate the string */
180 sendto_one(client, "%s", buf);
181 t = start;
182 }
183
184 if (member->flags & CHFL_CHANOP)
185 *t++ = '@';
186 if (member->flags & CHFL_HALFOP)
187 *t++ = '%';
188 if (member->flags & CHFL_VOICE)
189 *t++ = '+';
190
191 strcpy(t, member->client->id);
192
193 t += strlen(t);
194 *t++ = ' ';
195 }
196
197 /* Should always be non-NULL unless we have a kind of persistent channels */
198 if (channel->members.head)
199 --t; /* Take the space out */
200 *t = '\0';
201 sendto_one(client, "%s", buf);
202 }
203
204 /*! \brief Sends +b/+e/+I
205 * \param client Client pointer to server
206 * \param channel Pointer to channel
207 * \param list Pointer to list of modes to send
208 * \param flag Char flag flagging type of mode. Currently this can be 'b', e' or 'I'
209 */
210 static void
211 channel_send_mask_list(struct Client *client, const struct Channel *channel,
212 const dlink_list *list, const char flag)
213 {
214 dlink_node *node;
215 char mbuf[IRCD_BUFSIZE];
216 char pbuf[IRCD_BUFSIZE];
217 size_t tlen, mlen, cur_len;
218 char *pp = pbuf;
219
220 if (dlink_list_length(list) == 0)
221 return;
222
223 mlen = snprintf(mbuf, sizeof(mbuf), ":%s BMASK %ju %s %c :", me.id,
224 channel->creation_time, channel->name, flag);
225 cur_len = mlen;
226
227 DLINK_FOREACH(node, list->head)
228 {
229 const struct Ban *ban = node->data;
230
231 tlen = ban->banstr_len + 1; /* +1 for space */
232
233 /*
234 * Send buffer and start over if we cannot fit another ban
235 */
236 if (cur_len + (tlen - 1) > sizeof(pbuf) - 2)
237 {
238 *(pp - 1) = '\0'; /* Get rid of trailing space on buffer */
239 sendto_one(client, "%s%s", mbuf, pbuf);
240
241 cur_len = mlen;
242 pp = pbuf;
243 }
244
245 pp += snprintf(pp, sizeof(pbuf) - (pp - pbuf), "%s ", ban->banstr);
246 cur_len += tlen;
247 }
248
249 *(pp - 1) = '\0'; /* Get rid of trailing space on buffer */
250 sendto_one(client, "%s%s", mbuf, pbuf);
251 }
252
253 /*! \brief Send "client" a full list of the modes for channel channel
254 * \param client Pointer to client client
255 * \param channel Pointer to channel pointer
256 */
257 void
258 channel_send_modes(struct Client *client, const struct Channel *channel)
259 {
260 char modebuf[MODEBUFLEN] = "";
261 char parabuf[MODEBUFLEN] = "";
262
263 channel_modes(channel, client, modebuf, parabuf);
264 channel_send_members(client, channel, modebuf, parabuf);
265
266 channel_send_mask_list(client, channel, &channel->banlist, 'b');
267 channel_send_mask_list(client, channel, &channel->exceptlist, 'e');
268 channel_send_mask_list(client, channel, &channel->invexlist, 'I');
269 }
270
271 /*! \brief Check channel name for invalid characters
272 * \param name Pointer to channel name string
273 * \param local Indicates whether it's a local or remote creation
274 * \return 0 if invalid, 1 otherwise
275 */
276 bool
277 channel_check_name(const char *name, bool local)
278 {
279 const char *p = name;
280
281 assert(!EmptyString(p));
282
283 if (!IsChanPrefix(*p))
284 return false;
285
286 if (local == false || ConfigChannel.disable_fake_channels == 0)
287 {
288 while (*++p)
289 if (!IsChanChar(*p))
290 return false;
291 }
292 else
293 {
294 while (*++p)
295 if (!IsVisibleChanChar(*p))
296 return false;
297 }
298
299 return p - name <= CHANNELLEN;
300 }
301
302 void
303 remove_ban(struct Ban *ban, dlink_list *list)
304 {
305 dlinkDelete(&ban->node, list);
306 xfree(ban);
307 }
308
309 /* channel_free_mask_list()
310 *
311 * inputs - pointer to dlink_list
312 * output - NONE
313 * side effects -
314 */
315 static void
316 channel_free_mask_list(dlink_list *list)
317 {
318 while (list->head)
319 {
320 struct Ban *ban = list->head->data;
321 remove_ban(ban, list);
322 }
323 }
324
325 /*! \brief Get Channel block for name (and allocate a new channel
326 * block, if it didn't exist before)
327 * \param name Channel name
328 * \return Channel block
329 */
330 struct Channel *
331 channel_make(const char *name)
332 {
333 assert(!EmptyString(name));
334
335 struct Channel *channel = xcalloc(sizeof(*channel));
336 channel->hnextch = channel;
337 /* Doesn't hurt to set it here */
338 channel->creation_time = event_base->time.sec_real;
339 channel->last_join_time = event_base->time.sec_monotonic;
340
341 /* Cache channel name length to avoid repetitive strlen() calls. */
342 channel->name_len = strlcpy(channel->name, name, sizeof(channel->name));
343 if (channel->name_len >= sizeof(channel->name))
344 channel->name_len = sizeof(channel->name) - 1;
345
346 dlinkAdd(channel, &channel->node, &channel_list);
347 hash_add_channel(channel);
348
349 return channel;
350 }
351
352 /*! \brief Walk through this channel, and destroy it.
353 * \param channel Channel pointer
354 */
355 void
356 channel_free(struct Channel *channel)
357 {
358 invite_clear_list(&channel->invites);
359
360 /* Free ban/exception/invex lists */
361 channel_free_mask_list(&channel->banlist);
362 channel_free_mask_list(&channel->exceptlist);
363 channel_free_mask_list(&channel->invexlist);
364
365 dlinkDelete(&channel->node, &channel_list);
366 hash_del_channel(channel);
367
368 assert(channel->hnextch == channel);
369
370 assert(channel->node.prev == NULL);
371 assert(channel->node.next == NULL);
372
373 assert(dlink_list_length(&channel->members_local) == 0);
374 assert(channel->members_local.head == NULL);
375 assert(channel->members_local.tail == NULL);
376
377 assert(dlink_list_length(&channel->members) == 0);
378 assert(channel->members.head == NULL);
379 assert(channel->members.tail == NULL);
380
381 assert(dlink_list_length(&channel->invites) == 0);
382 assert(channel->invites.head == NULL);
383 assert(channel->invites.tail == NULL);
384
385 assert(dlink_list_length(&channel->banlist) == 0);
386 assert(channel->banlist.head == NULL);
387 assert(channel->banlist.tail == NULL);
388
389 assert(dlink_list_length(&channel->exceptlist) == 0);
390 assert(channel->exceptlist.head == NULL);
391 assert(channel->exceptlist.tail == NULL);
392
393 assert(dlink_list_length(&channel->invexlist) == 0);
394 assert(channel->invexlist.head == NULL);
395 assert(channel->invexlist.tail == NULL);
396
397 xfree(channel);
398 }
399
400 /*!
401 * \param channel Pointer to channel
402 * \return String pointer "=" if public, "@" if secret else "*"
403 */
404 static const char *
405 channel_pub_or_secret(const struct Channel *channel)
406 {
407 if (SecretChannel(channel))
408 return "@";
409 if (PrivateChannel(channel))
410 return "*";
411 return "=";
412 }
413
414 /*! \brief lists all names on given channel
415 * \param client Pointer to client struct requesting names
416 * \param channel Pointer to channel block
417 * \param show_eon Show RPL_ENDOFNAMES numeric or not
418 * (don't want it with /names with no params)
419 */
420 void
421 channel_member_names(struct Client *client, struct Channel *channel, bool show_eon)
422 {
423 dlink_node *node;
424 char buf[IRCD_BUFSIZE + 1];
425 char *bufptr = buf;
426 size_t masklen = 0;
427 bool is_member = IsMember(client, channel);
428 bool multi_prefix = HasCap(client, CAP_MULTI_PREFIX) != 0;
429 bool uhnames = HasCap(client, CAP_UHNAMES) != 0;
430
431 assert(IsClient(client));
432
433 if (PubChannel(channel) || is_member == true)
434 {
435 /* :me.name 353 source_p->name @ channel->name :+nick1 @nick2 %nick3 ...\r\n */
436 /* 1 23456 789 01 2 3 */
437 size_t len = strlen(me.name) + strlen(client->name) + channel->name_len + 13;
438
439 DLINK_FOREACH(node, channel->members.head)
440 {
441 const struct ChannelMember *member = node->data;
442
443 if (HasUMode(member->client, UMODE_INVISIBLE) && is_member == false)
444 continue;
445
446 if (uhnames == true)
447 masklen = strlen(member->client->name) + strlen(member->client->username) +
448 strlen(member->client->host) + 3; /* +3 for ! + @ + space */
449 else
450 masklen = strlen(member->client->name) + 1; /* +1 for space */
451
452 if (multi_prefix == true)
453 {
454 if (member->flags & CHFL_CHANOP)
455 ++masklen;
456 if (member->flags & CHFL_HALFOP)
457 ++masklen;
458 if (member->flags & CHFL_VOICE)
459 ++masklen;
460 }
461 else
462 {
463 if (member->flags & (CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE))
464 ++masklen;
465 }
466
467 if ((bufptr - buf) + masklen + len > sizeof(buf))
468 {
469 *(bufptr - 1) = '\0';
470 sendto_one_numeric(client, &me, RPL_NAMREPLY,
471 channel_pub_or_secret(channel), channel->name, buf);
472 bufptr = buf;
473 }
474
475 if (uhnames == true)
476 bufptr += snprintf(bufptr, sizeof(buf) - (bufptr - buf), "%s%s!%s@%s ",
477 get_member_status(member, multi_prefix),
478 member->client->name, member->client->username,
479 member->client->host);
480 else
481 bufptr += snprintf(bufptr, sizeof(buf) - (bufptr - buf), "%s%s ",
482 get_member_status(member, multi_prefix),
483 member->client->name);
484 }
485
486 if (bufptr != buf)
487 {
488 *(bufptr - 1) = '\0';
489 sendto_one_numeric(client, &me, RPL_NAMREPLY,
490 channel_pub_or_secret(channel), channel->name, buf);
491 }
492 }
493
494 if (show_eon == true)
495 sendto_one_numeric(client, &me, RPL_ENDOFNAMES, channel->name);
496 }
497
498 /* get_member_status()
499 *
500 * inputs - pointer to struct ChannelMember
501 * - YES if we can combine different flags
502 * output - string either @, +, % or "" depending on whether
503 * chanop, voiced or user
504 * side effects -
505 *
506 * NOTE: Returned string is usually a static buffer
507 * (like in client_get_name)
508 */
509 const char *
510 get_member_status(const struct ChannelMember *member, bool combine)
511 {
512 static char buffer[CMEMBER_STATUS_FLAGS_LEN + 1]; /* +1 for \0 */
513 char *p = buffer;
514
515 if (member->flags & CHFL_CHANOP)
516 {
517 if (combine == false)
518 return "@";
519 *p++ = '@';
520 }
521
522 if (member->flags & CHFL_HALFOP)
523 {
524 if (combine == false)
525 return "%";
526 *p++ = '%';
527 }
528
529 if (member->flags & CHFL_VOICE)
530 *p++ = '+';
531 *p = '\0';
532
533 return buffer;
534 }
535
536 /*!
537 * \param client Pointer to Client to check
538 * \param list Pointer to ban list to search
539 * \return 1 if ban found for given n!u\@h mask, 0 otherwise
540 */
541 static bool
542 ban_matches(struct Client *client, struct Channel *channel, struct Ban *ban)
543 {
544 /* Is a matching extban, call custom match handler */
545 if (ban->extban & extban_matching_mask())
546 {
547 struct Extban *extban = extban_find_flag(ban->extban & extban_matching_mask());
548 if (extban == NULL)
549 return false;
550
551 if (extban->matches == NULL || extban->matches(client, channel, ban) == EXTBAN_NO_MATCH)
552 return false;
553
554 return true;
555 }
556
557 if (match(ban->name, client->name) == 0 && match(ban->user, client->username) == 0)
558 {
559 switch (ban->type)
560 {
561 case HM_HOST:
562 if (match(ban->host, client->realhost) == 0 ||
563 match(ban->host, client->sockhost) == 0 || match(ban->host, client->host) == 0)
564 return true;
565 break;
566 case HM_IPV4:
567 if (client->ip.ss.ss_family == AF_INET)
568 if (match_ipv4(&client->ip, &ban->addr, ban->bits))
569 return true;
570 break;
571 case HM_IPV6:
572 if (client->ip.ss.ss_family == AF_INET6)
573 if (match_ipv6(&client->ip, &ban->addr, ban->bits))
574 return true;
575 break;
576 default:
577 assert(0);
578 }
579 }
580
581 return false;
582 }
583
584 bool
585 find_bmask(struct Client *client, struct Channel *channel, const dlink_list *list, struct Extban *extban)
586 {
587 dlink_node *node;
588
589 DLINK_FOREACH(node, list->head)
590 {
591 struct Ban *ban = node->data;
592
593 /* Looking for a specific type of extban? */
594 if (extban)
595 {
596 if (!(ban->extban & extban->flag))
597 continue;
598 }
599 else
600 {
601 /*
602 * Acting extbans have their own time they act and are not general purpose bans,
603 * so skip them unless we are hunting them.
604 */
605 if (ban->extban & extban_acting_mask())
606 continue;
607 }
608
609 bool matches = ban_matches(client, channel, ban);
610 if (matches == false)
611 continue;
612
613 return true;
614 }
615
616 return false;
617 }
618
619 /*!
620 * \param channel Pointer to channel block
621 * \param client Pointer to client to check access fo
622 * \return 0 if not banned, 1 otherwise
623 */
624 bool
625 is_banned(struct Channel *channel, struct Client *client)
626 {
627 if (find_bmask(client, channel, &channel->banlist, NULL) == true)
628 return find_bmask(client, channel, &channel->exceptlist, NULL) == false;
629 return false;
630 }
631
632 /*! Tests if a client can join a certain channel
633 * \param client Pointer to client attempting to join
634 * \param channel Pointer to channel
635 * \param key Key sent by client attempting to join if present
636 * \return ERR_BANNEDFROMCHAN, ERR_INVITEONLYCHAN, ERR_CHANNELISFULL
637 * or 0 if allowed to join.
638 */
639 static int
640 can_join(struct Client *client, struct Channel *channel, const char *key)
641 {
642 if (HasCMode(channel, MODE_SECUREONLY) && !HasUMode(client, UMODE_SECURE))
643 return ERR_SECUREONLYCHAN;
644
645 if (HasCMode(channel, MODE_REGONLY) && !HasUMode(client, UMODE_REGISTERED))
646 return ERR_NEEDREGGEDNICK;
647
648 if (HasCMode(channel, MODE_OPERONLY) && !HasUMode(client, UMODE_OPER))
649 return ERR_OPERONLYCHAN;
650
651 if (HasCMode(channel, MODE_INVITEONLY))
652 if (invite_find(channel, client) == NULL)
653 if (find_bmask(client, channel, &channel->invexlist, NULL) == false)
654 return ERR_INVITEONLYCHAN;
655
656 if (channel->mode.key[0] && (key == NULL || strcmp(channel->mode.key, key)))
657 return ERR_BADCHANNELKEY;
658
659 if (channel->mode.limit && dlink_list_length(&channel->members) >=
660 channel->mode.limit)
661 return ERR_CHANNELISFULL;
662
663 if (is_banned(channel, client) == true)
664 return ERR_BANNEDFROMCHAN;
665
666 return extban_join_can_join(channel, client, NULL);
667 }
668
669 int
670 has_member_flags(const struct ChannelMember *member, const unsigned int flags)
671 {
672 return member && (member->flags & flags);
673 }
674
675 struct ChannelMember *
676 find_channel_link(const struct Client *client, const struct Channel *channel)
677 {
678 dlink_node *node;
679
680 if (!IsClient(client))
681 return NULL;
682
683 /* Take the shortest of the two lists */
684 if (dlink_list_length(&channel->members) < dlink_list_length(&client->channel))
685 {
686 DLINK_FOREACH(node, channel->members.head)
687 if (((struct ChannelMember *)node->data)->client == client)
688 return node->data;
689 }
690 else
691 {
692 DLINK_FOREACH(node, client->channel.head)
693 if (((struct ChannelMember *)node->data)->channel == channel)
694 return node->data;
695 }
696
697 return NULL;
698 }
699
700 /*! Checks if a message contains control codes
701 * \param message The actual message string the client wants to send
702 * \return 1 if the message does contain any control codes, 0 otherwise
703 */
704 static bool
705 msg_has_ctrls(const char *message)
706 {
707 const unsigned char *p = (const unsigned char *)message;
708
709 for (; *p; ++p)
710 {
711 if (*p > 31 || *p == 1)
712 continue; /* No control code or CTCP */
713
714 if (*p == 27) /* Escape */
715 {
716 /* ISO 2022 charset shift sequence */
717 if (*(p + 1) == '$' ||
718 *(p + 1) == '(')
719 {
720 ++p;
721 continue;
722 }
723 }
724
725 return true; /* Control code */
726 }
727
728 return false; /* No control code found */
729 }
730
731 /*! Tests if a client can send to a channel
732 * \param channel Pointer to Channel struct
733 * \param client Pointer to Client struct
734 * \param member Pointer to Membership struct (can be NULL)
735 * \param message The actual message string the client wants to send
736 * \return CAN_SEND_OPV if op or voiced on channel\n
737 * CAN_SEND_NONOP if can send to channel but is not an op\n
738 * ERR_CANNOTSENDTOCHAN or ERR_NEEDREGGEDNICK if they cannot send to channel\n
739 */
740 int
741 can_send(struct Channel *channel, struct Client *client,
742 struct ChannelMember *member, const char *message, bool notice)
743 {
744 const struct ResvItem *resv;
745
746 if (IsServer(client) || HasFlag(client, FLAGS_SERVICE))
747 return CAN_SEND_OPV;
748
749 if (MyConnect(client) && !HasFlag(client, FLAGS_EXEMPTRESV))
750 if (!(HasUMode(client, UMODE_OPER) && HasOFlag(client, OPER_FLAG_JOIN_RESV)))
751 if ((resv = resv_find(channel->name, match)) && resv_exempt_find(client, resv) == false)
752 return ERR_CANNOTSENDTOCHAN;
753
754 if (HasCMode(channel, MODE_NOCTRL) && msg_has_ctrls(message) == true)
755 return ERR_NOCTRLSONCHAN;
756
757 if (HasCMode(channel, MODE_NOCTCP))
758 if (*message == '\001' && strncmp(message + 1, "ACTION ", 7))
759 return ERR_NOCTCP;
760
761 if (member || (member = find_channel_link(client, channel)))
762 if (member->flags & (CHFL_CHANOP | CHFL_HALFOP | CHFL_VOICE))
763 return CAN_SEND_OPV;
764
765 if (member == NULL && HasCMode(channel, MODE_NOPRIVMSGS))
766 return ERR_CANNOTSENDTOCHAN;
767
768 if (HasCMode(channel, MODE_MODERATED))
769 return ERR_CANNOTSENDTOCHAN;
770
771 if (HasCMode(channel, MODE_MODREG) && !HasUMode(client, UMODE_REGISTERED))
772 return ERR_NEEDREGGEDNICK;
773
774 if (HasCMode(channel, MODE_NONOTICE) && notice == true)
775 return ERR_CANNOTSENDTOCHAN;
776
777 /* Cache can send if banned */
778 if (MyConnect(client))
779 {
780 if (member)
781 {
782 if (member->flags & CHFL_BAN_SILENCED)
783 return ERR_CANNOTSENDTOCHAN;
784
785 if (!(member->flags & CHFL_BAN_CHECKED))
786 {
787 if (is_banned(channel, client) == true)
788 {
789 member->flags |= (CHFL_BAN_CHECKED | CHFL_BAN_SILENCED);
790 return ERR_CANNOTSENDTOCHAN;
791 }
792
793 member->flags |= CHFL_BAN_CHECKED;
794 }
795 }
796 else if (is_banned(channel, client) == true)
797 return ERR_CANNOTSENDTOCHAN;
798 }
799
800 return extban_mute_can_send(channel, client, member);
801 }
802
803 /*! \brief Updates the client's oper_warn_count_down, warns the
804 * IRC operators if necessary, and updates
805 * join_leave_countdown as needed.
806 * \param client Pointer to struct Client to check
807 * \param name Channel name or NULL if this is a part.
808 */
809 void
810 check_spambot_warning(struct Client *client, const char *name)
811 {
812 if (GlobalSetOptions.spam_num &&
813 (client->connection->join_leave_count >= GlobalSetOptions.spam_num))
814 {
815 if (client->connection->oper_warn_count_down)
816 --client->connection->oper_warn_count_down;
817
818 if (client->connection->oper_warn_count_down == 0)
819 {
820 /* It's already known as a possible spambot */
821 if (name)
822 sendto_realops_flags(UMODE_BOTS, L_ALL, SEND_NOTICE,
823 "User %s (%s@%s) trying to join %s is a possible spambot",
824 client->name, client->username,
825 client->host, name);
826 else
827 sendto_realops_flags(UMODE_BOTS, L_ALL, SEND_NOTICE,
828 "User %s (%s@%s) is a possible spambot",
829 client->name, client->username,
830 client->host);
831 client->connection->oper_warn_count_down = OPER_SPAM_COUNTDOWN;
832 }
833 }
834 else
835 {
836 unsigned int t_delta = event_base->time.sec_monotonic - client->connection->last_leave_time;
837 if (t_delta > JOIN_LEAVE_COUNT_EXPIRE_TIME)
838 {
839 unsigned int decrement_count = (t_delta / JOIN_LEAVE_COUNT_EXPIRE_TIME);
840 if (decrement_count > client->connection->join_leave_count)
841 client->connection->join_leave_count = 0;
842 else
843 client->connection->join_leave_count -= decrement_count;
844 }
845 else
846 {
847 if ((event_base->time.sec_monotonic - client->connection->last_join_time) < GlobalSetOptions.spam_time)
848 ++client->connection->join_leave_count; /* It's a possible spambot */
849 }
850
851 if (name)
852 client->connection->last_join_time = event_base->time.sec_monotonic;
853 else
854 client->connection->last_leave_time = event_base->time.sec_monotonic;
855 }
856 }
857
858 /*! \brief Sets the channel topic for a certain channel
859 * \param channel Pointer to struct Channel
860 * \param topic The topic string
861 * \param topic_info n!u\@h formatted string of the topic setter
862 * \param topicts Timestamp on the topic
863 * \param local Whether the topic is set by a local client
864 */
865 void
866 channel_set_topic(struct Channel *channel, const char *topic,
867 const char *topic_info, uintmax_t topicts, bool local)
868 {
869 if (local == true)
870 strlcpy(channel->topic, topic, IRCD_MIN(sizeof(channel->topic), ConfigServerInfo.max_topic_length + 1));
871 else
872 strlcpy(channel->topic, topic, sizeof(channel->topic));
873
874 strlcpy(channel->topic_info, topic_info, sizeof(channel->topic_info));
875 channel->topic_time = topicts;
876 }
877
878 void
879 channel_do_join(struct Client *client, char *chan_list, char *key_list)
880 {
881 char *p = NULL;
882 const struct ResvItem *resv = NULL;
883 const struct ClassItem *const class = class_get_ptr(&client->connection->confs);
884 unsigned int flags = 0;
885
886 assert(MyClient(client));
887
888 for (const char *name = strtok_r(chan_list, ",", &p); name;
889 name = strtok_r(NULL, ",", &p))
890 {
891 const char *key = NULL;
892
893 /* If we have any more keys, take the first for this channel. */
894 if (!EmptyString(key_list) && (key_list = strchr(key = key_list, ',')))
895 *key_list++ = '\0';
896
897 /* Empty keys are the same as no keys. */
898 if (key && *key == '\0')
899 key = NULL;
900
901 if (channel_check_name(name, true) == false)
902 {
903 sendto_one_numeric(client, &me, ERR_BADCHANNAME, name);
904 continue;
905 }
906
907 if (!HasFlag(client, FLAGS_EXEMPTRESV) &&
908 !(HasUMode(client, UMODE_OPER) && HasOFlag(client, OPER_FLAG_JOIN_RESV)) &&
909 ((resv = resv_find(name, match)) && resv_exempt_find(client, resv) == false))
910 {
911 sendto_one_numeric(client, &me, ERR_CHANBANREASON, name, resv->reason);
912 sendto_realops_flags(UMODE_REJ, L_ALL, SEND_NOTICE,
913 "Forbidding reserved channel %s from user %s",
914 name, client_get_name(client, HIDE_IP));
915 continue;
916 }
917
918 if (dlink_list_length(&client->channel) >=
919 ((class->max_channels) ? class->max_channels : ConfigChannel.max_channels))
920 {
921 sendto_one_numeric(client, &me, ERR_TOOMANYCHANNELS, name);
922 break;
923 }
924
925 struct Channel *channel = hash_find_channel(name);
926 if (channel)
927 {
928 if (IsMember(client, channel))
929 continue;
930
931 /* can_join() checks for +i, +l, key, bans, etc. */
932 int ret = can_join(client, channel, key);
933 if (ret)
934 {
935 sendto_one_numeric(client, &me, ret, channel->name);
936 continue;
937 }
938
939 /*
940 * This should never be the case unless there is some sort of
941 * persistent channels.
942 */
943 if (dlink_list_length(&channel->members) == 0)
944 flags = CHFL_CHANOP;
945 else
946 flags = 0;
947 }
948 else
949 {
950 flags = CHFL_CHANOP;
951 channel = channel_make(name);
952 }
953
954 if (!HasUMode(client, UMODE_OPER))
955 check_spambot_warning(client, channel->name);
956
957 add_user_to_channel(channel, client, flags, true);
958
959 /*
960 * Set timestamp if appropriate, and propagate
961 */
962 if (flags == CHFL_CHANOP)
963 {
964 channel->creation_time = event_base->time.sec_real;
965 AddCMode(channel, MODE_TOPICLIMIT);
966 AddCMode(channel, MODE_NOPRIVMSGS);
967
968 sendto_server(NULL, 0, 0, ":%s SJOIN %ju %s +nt :@%s",
969 me.id, channel->creation_time,
970 channel->name, client->id);
971
972 /*
973 * Notify all other users on the new channel
974 */
975 sendto_channel_local(NULL, channel, 0, CAP_EXTENDED_JOIN, 0, ":%s!%s@%s JOIN %s %s :%s",
976 client->name, client->username,
977 client->host, channel->name, client->account, client->info);
978 sendto_channel_local(NULL, channel, 0, 0, CAP_EXTENDED_JOIN, ":%s!%s@%s JOIN :%s",
979 client->name, client->username,
980 client->host, channel->name);
981 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s +nt",
982 me.name, channel->name);
983 }
984 else
985 {
986 sendto_server(NULL, 0, 0, ":%s JOIN %ju %s +",
987 client->id, channel->creation_time,
988 channel->name);
989
990 sendto_channel_local(NULL, channel, 0, CAP_EXTENDED_JOIN, 0, ":%s!%s@%s JOIN %s %s :%s",
991 client->name, client->username,
992 client->host, channel->name, client->account, client->info);
993 sendto_channel_local(NULL, channel, 0, 0, CAP_EXTENDED_JOIN, ":%s!%s@%s JOIN :%s",
994 client->name, client->username,
995 client->host, channel->name);
996 }
997
998 if (client->away[0])
999 sendto_channel_local(client, channel, 0, CAP_AWAY_NOTIFY, 0,
1000 ":%s!%s@%s AWAY :%s",
1001 client->name, client->username,
1002 client->host, client->away);
1003
1004 struct Invite *invite = invite_find(channel, client);
1005 if (invite)
1006 invite_del(invite);
1007
1008 if (channel->topic[0])
1009 {
1010 sendto_one_numeric(client, &me, RPL_TOPIC, channel->name, channel->topic);
1011 sendto_one_numeric(client, &me, RPL_TOPICWHOTIME, channel->name,
1012 channel->topic_info, channel->topic_time);
1013 }
1014
1015 channel_member_names(client, channel, true);
1016
1017 client->connection->last_join_time = event_base->time.sec_monotonic;
1018 }
1019 }
1020
1021 /*! \brief Removes a client from a specific channel
1022 * \param client Pointer to client to remove
1023 * \param name Name of channel to remove from
1024 * \param reason Part reason to show
1025 */
1026 static void
1027 channel_part_one_client(struct Client *client, const char *name, const char *reason)
1028 {
1029 struct Channel *channel = hash_find_channel(name);
1030 if (channel == NULL)
1031 {
1032 sendto_one_numeric(client, &me, ERR_NOSUCHCHANNEL, name);
1033 return;
1034 }
1035
1036 struct ChannelMember *member = find_channel_link(client, channel);
1037 if (member == NULL)
1038 {
1039 sendto_one_numeric(client, &me, ERR_NOTONCHANNEL, channel->name);
1040 return;
1041 }
1042
1043 if (MyConnect(client) && !HasUMode(client, UMODE_OPER))
1044 check_spambot_warning(client, NULL);
1045
1046 /*
1047 * Remove user from the old channel (if any). Only allow /part reasons in -m chans.
1048 */
1049 if (*reason && (!MyConnect(client) ||
1050 ((client->connection->created_monotonic +
1051 ConfigGeneral.anti_spam_exit_message_time) < event_base->time.sec_monotonic &&
1052 can_send(channel, client, member, reason, false) < 0)))
1053 {
1054 sendto_server(client, 0, 0, ":%s PART %s :%s",
1055 client->id, channel->name, reason);
1056 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s!%s@%s PART %s :%s",
1057 client->name, client->username,
1058 client->host, channel->name, reason);
1059 }
1060 else
1061 {
1062 sendto_server(client, 0, 0, ":%s PART %s",
1063 client->id, channel->name);
1064 sendto_channel_local(NULL, channel, 0, 0, 0, ":%s!%s@%s PART %s",
1065 client->name, client->username,
1066 client->host, channel->name);
1067 }
1068
1069 remove_user_from_channel(member);
1070 }
1071
1072 void
1073 channel_do_part(struct Client *client, char *channel, const char *reason)
1074 {
1075 char *p = NULL;
1076 char buf[KICKLEN + 1] = "";
1077
1078 assert(IsClient(client));
1079
1080 if (!EmptyString(reason))
1081 strlcpy(buf, reason, sizeof(buf));
1082
1083 for (const char *name = strtok_r(channel, ",", &p); name;
1084 name = strtok_r(NULL, ",", &p))
1085 channel_part_one_client(client, name, buf);
1086 }

Properties

Name Value
svn:eol-style native
svn:keywords Id