1 |
|
/* |
2 |
|
* ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd) |
3 |
|
* |
4 |
< |
* Copyright (c) 1997-2015 ircd-hybrid development team |
4 |
> |
* Copyright (c) 1997-2016 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 |
31 |
|
#include "client.h" |
32 |
|
#include "hash.h" |
33 |
|
#include "conf.h" |
34 |
+ |
#include "conf_resv.h" |
35 |
|
#include "hostmask.h" |
36 |
|
#include "irc_string.h" |
37 |
|
#include "ircd.h" |
42 |
|
#include "memory.h" |
43 |
|
#include "mempool.h" |
44 |
|
#include "misc.h" |
44 |
– |
#include "resv.h" |
45 |
|
|
46 |
|
|
47 |
|
dlink_list channel_list; |
48 |
|
mp_pool_t *ban_pool; /*! \todo ban_pool shouldn't be a global var */ |
49 |
|
|
50 |
< |
static mp_pool_t *member_pool, *channel_pool; |
50 |
> |
static mp_pool_t *member_pool, *channel_pool, *invite_pool; |
51 |
|
|
52 |
|
|
53 |
|
/*! \brief Initializes the channel blockheap, adds known channel CAPAB |
58 |
|
add_capability("EX", CAPAB_EX); |
59 |
|
add_capability("IE", CAPAB_IE); |
60 |
|
|
61 |
+ |
invite_pool = mp_pool_new(sizeof(struct Invite), MP_CHUNK_SIZE_INVITE); |
62 |
|
channel_pool = mp_pool_new(sizeof(struct Channel), MP_CHUNK_SIZE_CHANNEL); |
63 |
|
ban_pool = mp_pool_new(sizeof(struct Ban), MP_CHUNK_SIZE_BAN); |
64 |
|
member_pool = mp_pool_new(sizeof(struct Membership), MP_CHUNK_SIZE_MEMBER); |
75 |
|
add_user_to_channel(struct Channel *chptr, struct Client *client_p, |
76 |
|
unsigned int flags, int flood_ctrl) |
77 |
|
{ |
77 |
– |
struct Membership *member = NULL; |
78 |
– |
|
78 |
|
assert(IsClient(client_p)); |
79 |
|
|
80 |
|
if (GlobalSetOptions.joinfloodtime > 0) |
108 |
|
chptr->last_join_time = CurrentTime; |
109 |
|
} |
110 |
|
|
111 |
< |
member = mp_pool_get(member_pool); |
111 |
> |
struct Membership *member = mp_pool_get(member_pool); |
112 |
|
member->client_p = client_p; |
113 |
|
member->chptr = chptr; |
114 |
|
member->flags = flags; |
223 |
|
int tlen, mlen, cur_len; |
224 |
|
char *pp = pbuf; |
225 |
|
|
226 |
< |
if (!list->length) |
226 |
> |
if (!dlink_list_length(list)) |
227 |
|
return; |
228 |
|
|
229 |
|
mlen = snprintf(mbuf, sizeof(mbuf), ":%s BMASK %ju %s %c :", me.id, |
321 |
|
static void |
322 |
|
channel_free_mask_list(dlink_list *list) |
323 |
|
{ |
324 |
< |
dlink_node *node = NULL, *node_next = NULL; |
325 |
< |
|
326 |
< |
DLINK_FOREACH_SAFE(node, node_next, list->head) |
327 |
< |
remove_ban(node->data, list); |
324 |
> |
while (list->head) |
325 |
> |
{ |
326 |
> |
struct Ban *ban = list->head->data; |
327 |
> |
remove_ban(ban, list); |
328 |
> |
} |
329 |
|
} |
330 |
|
|
331 |
|
/*! \brief Get Channel block for name (and allocate a new channel |
346 |
|
chptr->creationtime = CurrentTime; |
347 |
|
chptr->last_join_time = CurrentTime; |
348 |
|
|
349 |
< |
strlcpy(chptr->name, name, sizeof(chptr->name)); |
350 |
< |
dlinkAdd(chptr, &chptr->node, &channel_list); |
349 |
> |
chptr->name_len = strlcpy(chptr->name, name, sizeof(chptr->name)); |
350 |
> |
if (chptr->name_len >= sizeof(chptr->name)) |
351 |
> |
chptr->name_len = sizeof(chptr->name) - 1; |
352 |
|
|
353 |
+ |
dlinkAdd(chptr, &chptr->node, &channel_list); |
354 |
|
hash_add_channel(chptr); |
355 |
|
|
356 |
|
return chptr; |
471 |
|
sendto_one_numeric(client_p, &me, RPL_ENDOFNAMES, chptr->name); |
472 |
|
} |
473 |
|
|
474 |
+ |
static struct Invite * |
475 |
+ |
find_invite(struct Channel *chptr, struct Client *client_p) |
476 |
+ |
{ |
477 |
+ |
dlink_node *node, *node_next; |
478 |
+ |
dlink_list *list; |
479 |
+ |
|
480 |
+ |
if (dlink_list_length(&client_p->connection->invited) < dlink_list_length(&chptr->invites)) |
481 |
+ |
list = &client_p->connection->invited; |
482 |
+ |
else |
483 |
+ |
list = &chptr->invites; |
484 |
+ |
|
485 |
+ |
DLINK_FOREACH_SAFE(node, node_next, list->head) |
486 |
+ |
{ |
487 |
+ |
struct Invite *invite = node->data; |
488 |
+ |
|
489 |
+ |
if (invite->chptr == chptr && invite->client_p == client_p) |
490 |
+ |
return invite; |
491 |
+ |
} |
492 |
+ |
|
493 |
+ |
return NULL; |
494 |
+ |
} |
495 |
+ |
|
496 |
|
/*! \brief Adds client to invite list |
497 |
|
* \param chptr Pointer to channel block |
498 |
|
* \param client_p Pointer to client to add invite to |
500 |
|
void |
501 |
|
add_invite(struct Channel *chptr, struct Client *client_p) |
502 |
|
{ |
503 |
< |
assert(IsClient(client_p)); |
503 |
> |
struct Invite *invite; |
504 |
|
|
505 |
< |
del_invite(chptr, client_p); |
505 |
> |
if ((invite = find_invite(chptr, client_p))) |
506 |
> |
del_invite(invite); |
507 |
|
|
508 |
< |
/* |
509 |
< |
* Delete last link in chain if the list is max length |
510 |
< |
*/ |
511 |
< |
if (dlink_list_length(&client_p->connection->invited) >= |
512 |
< |
ConfigChannel.max_channels) |
513 |
< |
del_invite(client_p->connection->invited.tail->data, client_p); |
508 |
> |
invite = mp_pool_get(invite_pool); |
509 |
> |
invite->client_p = client_p; |
510 |
> |
invite->chptr = chptr; |
511 |
> |
invite->when = CurrentTime; |
512 |
> |
|
513 |
> |
/* Delete last link in chain if the list is max length */ |
514 |
> |
while (dlink_list_length(&client_p->connection->invited) && |
515 |
> |
dlink_list_length(&client_p->connection->invited) >= ConfigChannel.max_channels) |
516 |
> |
del_invite(client_p->connection->invited.tail->data); |
517 |
|
|
518 |
|
/* Add client to channel invite list */ |
519 |
< |
dlinkAdd(client_p, make_dlink_node(), &chptr->invites); |
519 |
> |
dlinkAdd(invite, &invite->chan_node, &chptr->invites); |
520 |
|
|
521 |
|
/* Add channel to the end of the client invite list */ |
522 |
< |
dlinkAdd(chptr, make_dlink_node(), &client_p->connection->invited); |
522 |
> |
dlinkAdd(invite, &invite->user_node, &client_p->connection->invited); |
523 |
|
} |
524 |
|
|
525 |
|
/*! \brief Delete Invite block from channel invite list |
526 |
|
* and client invite list |
527 |
< |
* \param chptr Pointer to Channel struct |
500 |
< |
* \param client_p Pointer to client to remove invites from |
527 |
> |
* \param invite Pointer to Invite struct |
528 |
|
*/ |
529 |
|
void |
530 |
< |
del_invite(struct Channel *chptr, struct Client *client_p) |
530 |
> |
del_invite(struct Invite *invite) |
531 |
|
{ |
532 |
< |
dlink_node *node = NULL; |
533 |
< |
|
507 |
< |
if ((node = dlinkFindDelete(&client_p->connection->invited, chptr))) |
508 |
< |
free_dlink_node(node); |
532 |
> |
dlinkDelete(&invite->user_node, &invite->client_p->connection->invited); |
533 |
> |
dlinkDelete(&invite->chan_node, &invite->chptr->invites); |
534 |
|
|
535 |
< |
if ((node = dlinkFindDelete(&chptr->invites, client_p))) |
536 |
< |
free_dlink_node(node); |
535 |
> |
/* Release memory pointed to by 'invite' */ |
536 |
> |
mp_pool_release(invite); |
537 |
|
} |
538 |
|
|
539 |
|
/*! \brief Removes all invites of a specific channel |
542 |
|
void |
543 |
|
clear_invites_channel(struct Channel *chptr) |
544 |
|
{ |
545 |
< |
dlink_node *node = NULL, *node_next = NULL; |
546 |
< |
|
522 |
< |
DLINK_FOREACH_SAFE(node, node_next, chptr->invites.head) |
523 |
< |
del_invite(chptr, node->data); |
545 |
> |
while (chptr->invites.head) |
546 |
> |
del_invite(chptr->invites.head->data); |
547 |
|
} |
548 |
|
|
549 |
|
/*! \brief Removes all invites of a specific client |
552 |
|
void |
553 |
|
clear_invites_client(struct Client *client_p) |
554 |
|
{ |
555 |
< |
dlink_node *node = NULL, *node_next = NULL; |
556 |
< |
|
534 |
< |
DLINK_FOREACH_SAFE(node, node_next, client_p->connection->invited.head) |
535 |
< |
del_invite(node->data, client_p); |
555 |
> |
while (client_p->connection->invited.head) |
556 |
> |
del_invite(client_p->connection->invited.head->data); |
557 |
|
} |
558 |
|
|
559 |
|
/* get_member_status() |
570 |
|
const char * |
571 |
|
get_member_status(const struct Membership *member, const int combine) |
572 |
|
{ |
573 |
< |
static char buffer[4]; /* 4 for @%+\0 */ |
573 |
> |
static char buffer[CMEMBER_STATUS_FLAGS_LEN + 1]; /* +1 for \0 */ |
574 |
|
char *p = buffer; |
575 |
|
|
576 |
|
if (member->flags & CHFL_CHANOP) |
600 |
|
* \return 1 if ban found for given n!u\@h mask, 0 otherwise |
601 |
|
*/ |
602 |
|
static int |
603 |
< |
find_bmask(const struct Client *client_p, const dlink_list *const list) |
603 |
> |
find_bmask(const struct Client *client_p, const dlink_list *list) |
604 |
|
{ |
605 |
|
const dlink_node *node = NULL; |
606 |
|
|
657 |
|
* \return ERR_BANNEDFROMCHAN, ERR_INVITEONLYCHAN, ERR_CHANNELISFULL |
658 |
|
* or 0 if allowed to join. |
659 |
|
*/ |
660 |
< |
int |
661 |
< |
can_join(struct Client *client_p, const struct Channel *chptr, const char *key) |
660 |
> |
static int |
661 |
> |
can_join(struct Client *client_p, struct Channel *chptr, const char *key) |
662 |
|
{ |
663 |
|
if ((chptr->mode.mode & MODE_SSLONLY) && !HasUMode(client_p, UMODE_SSL)) |
664 |
|
return ERR_SSLONLYCHAN; |
670 |
|
return ERR_OPERONLYCHAN; |
671 |
|
|
672 |
|
if (chptr->mode.mode & MODE_INVITEONLY) |
673 |
< |
if (!dlinkFind(&client_p->connection->invited, chptr)) |
673 |
> |
if (!find_invite(chptr, client_p)) |
674 |
|
if (!find_bmask(client_p, &chptr->invexlist)) |
675 |
|
return ERR_INVITEONLYCHAN; |
676 |
|
|
761 |
|
can_send(struct Channel *chptr, struct Client *client_p, |
762 |
|
struct Membership *member, const char *message, int notice) |
763 |
|
{ |
764 |
< |
const struct MaskItem *conf = NULL; |
764 |
> |
const struct ResvItem *resv = NULL; |
765 |
|
|
766 |
|
if (IsServer(client_p) || HasFlag(client_p, FLAGS_SERVICE)) |
767 |
|
return CAN_SEND_OPV; |
768 |
|
|
769 |
< |
if (MyClient(client_p) && !HasFlag(client_p, FLAGS_EXEMPTRESV)) |
769 |
> |
if (MyConnect(client_p) && !HasFlag(client_p, FLAGS_EXEMPTRESV)) |
770 |
|
if (!(HasUMode(client_p, UMODE_OPER) && HasOFlag(client_p, OPER_FLAG_JOIN_RESV))) |
771 |
< |
if ((conf = match_find_resv(chptr->name)) && !resv_find_exempt(client_p, conf)) |
771 |
> |
if ((resv = resv_find(chptr->name, match)) && !resv_exempt_find(client_p, resv)) |
772 |
|
return ERR_CANNOTSENDTOCHAN; |
773 |
|
|
774 |
|
if ((chptr->mode.mode & MODE_NOCTRL) && msg_has_ctrls(message)) |
795 |
|
return ERR_CANNOTSENDTOCHAN; |
796 |
|
|
797 |
|
/* Cache can send if banned */ |
798 |
< |
if (MyClient(client_p)) |
798 |
> |
if (MyConnect(client_p)) |
799 |
|
{ |
800 |
|
if (member) |
801 |
|
{ |
892 |
|
*/ |
893 |
|
void |
894 |
|
channel_set_topic(struct Channel *chptr, const char *topic, |
895 |
< |
const char *topic_info, time_t topicts, int local) |
895 |
> |
const char *topic_info, uintmax_t topicts, int local) |
896 |
|
{ |
897 |
|
if (local) |
898 |
|
strlcpy(chptr->topic, topic, IRCD_MIN(sizeof(chptr->topic), ConfigServerInfo.max_topic_length + 1)); |
915 |
|
void |
916 |
|
channel_do_join_0(struct Client *client_p) |
917 |
|
{ |
897 |
– |
dlink_node *node = NULL, *node_next = NULL; |
898 |
– |
|
918 |
|
if (client_p->channel.head) |
919 |
|
if (MyConnect(client_p) && !HasUMode(client_p, UMODE_OPER)) |
920 |
|
check_spambot_warning(client_p, NULL); |
921 |
|
|
922 |
< |
DLINK_FOREACH_SAFE(node, node_next, client_p->channel.head) |
922 |
> |
while (client_p->channel.head) |
923 |
|
{ |
924 |
< |
struct Channel *chptr = ((struct Membership *)node->data)->chptr; |
924 |
> |
struct Membership *member = client_p->channel.head->data; |
925 |
|
|
926 |
|
sendto_server(client_p, 0, 0, ":%s PART %s", |
927 |
< |
client_p->id, chptr->name); |
928 |
< |
sendto_channel_local(NULL, chptr, 0, 0, 0, ":%s!%s@%s PART %s", |
927 |
> |
client_p->id, member->chptr->name); |
928 |
> |
sendto_channel_local(NULL, member->chptr, 0, 0, 0, ":%s!%s@%s PART %s", |
929 |
|
client_p->name, client_p->username, |
930 |
< |
client_p->host, chptr->name); |
930 |
> |
client_p->host, member->chptr->name); |
931 |
|
|
932 |
< |
remove_user_from_channel(node->data); |
932 |
> |
remove_user_from_channel(member); |
933 |
|
} |
934 |
|
} |
935 |
|
|
968 |
|
channel_do_join(struct Client *client_p, char *channel, char *key_list) |
969 |
|
{ |
970 |
|
char *p = NULL; |
952 |
– |
char *chan = NULL; |
971 |
|
char *chan_list = NULL; |
972 |
|
struct Channel *chptr = NULL; |
973 |
< |
struct MaskItem *conf = NULL; |
973 |
> |
const struct ResvItem *resv = NULL; |
974 |
|
const struct ClassItem *const class = get_class_ptr(&client_p->connection->confs); |
957 |
– |
int i = 0; |
975 |
|
unsigned int flags = 0; |
976 |
|
|
977 |
|
assert(IsClient(client_p)); |
978 |
|
|
979 |
|
chan_list = channel_find_last0(client_p, channel); |
980 |
|
|
981 |
< |
for (chan = strtok_r(chan_list, ",", &p); chan; |
982 |
< |
chan = strtok_r(NULL, ",", &p)) |
981 |
> |
for (const char *chan = strtok_r(chan_list, ",", &p); chan; |
982 |
> |
chan = strtok_r(NULL, ",", &p)) |
983 |
|
{ |
984 |
|
const char *key = NULL; |
985 |
|
|
999 |
|
|
1000 |
|
if (!HasFlag(client_p, FLAGS_EXEMPTRESV) && |
1001 |
|
!(HasUMode(client_p, UMODE_OPER) && HasOFlag(client_p, OPER_FLAG_JOIN_RESV)) && |
1002 |
< |
((conf = match_find_resv(chan)) && !resv_find_exempt(client_p, conf))) |
1002 |
> |
((resv = resv_find(chan, match)) && !resv_exempt_find(client_p, resv))) |
1003 |
|
{ |
1004 |
< |
++conf->count; |
988 |
< |
sendto_one_numeric(client_p, &me, ERR_CHANBANREASON, chan, conf->reason); |
1004 |
> |
sendto_one_numeric(client_p, &me, ERR_CHANBANREASON, chan, resv->reason); |
1005 |
|
sendto_realops_flags(UMODE_REJ, L_ALL, SEND_NOTICE, |
1006 |
|
"Forbidding reserved channel %s from user %s", |
1007 |
|
chan, get_client_name(client_p, HIDE_IP)); |
1020 |
|
if (IsMember(client_p, chptr)) |
1021 |
|
continue; |
1022 |
|
|
1023 |
< |
/* |
1024 |
< |
* can_join checks for +i key, bans. |
1025 |
< |
*/ |
1010 |
< |
if ((i = can_join(client_p, chptr, key))) |
1023 |
> |
/* can_join() checks for +i, +l, key, bans, etc. */ |
1024 |
> |
int ret = can_join(client_p, chptr, key); |
1025 |
> |
if (ret) |
1026 |
|
{ |
1027 |
< |
sendto_one_numeric(client_p, &me, i, chptr->name); |
1027 |
> |
sendto_one_numeric(client_p, &me, ret, chptr->name); |
1028 |
|
continue; |
1029 |
|
} |
1030 |
|
|
1099 |
|
client_p->host, client_p->away); |
1100 |
|
} |
1101 |
|
|
1102 |
< |
del_invite(chptr, client_p); |
1102 |
> |
struct Invite *invite = find_invite(chptr, client_p); |
1103 |
> |
if (invite) |
1104 |
> |
del_invite(invite); |
1105 |
|
|
1106 |
|
if (chptr->topic[0]) |
1107 |
|
{ |
1117 |
|
} |
1118 |
|
|
1119 |
|
/*! \brief Removes a client from a specific channel |
1120 |
< |
* \param client_p Pointer to source client to remove |
1120 |
> |
* \param client_p Pointer to client to remove |
1121 |
|
* \param name Name of channel to remove from |
1122 |
|
* \param reason Part reason to show |
1123 |
|
*/ |
1147 |
|
* only allow /part reasons in -m chans |
1148 |
|
*/ |
1149 |
|
if (*reason && (!MyConnect(client_p) || |
1150 |
< |
((can_send(chptr, client_p, member, reason, 0) < 0 && |
1151 |
< |
(client_p->connection->firsttime + ConfigGeneral.anti_spam_exit_message_time) |
1152 |
< |
< CurrentTime)))) |
1150 |
> |
((client_p->connection->firsttime + |
1151 |
> |
ConfigGeneral.anti_spam_exit_message_time) < CurrentTime && |
1152 |
> |
can_send(chptr, client_p, member, reason, 0) < 0))) |
1153 |
|
{ |
1154 |
|
sendto_server(client_p, 0, 0, ":%s PART %s :%s", |
1155 |
|
client_p->id, chptr->name, reason); |