43 |
|
#include "modules.h" |
44 |
|
|
45 |
|
|
46 |
– |
struct entity |
47 |
– |
{ |
48 |
– |
struct Channel *chptr; |
49 |
– |
char *key; |
50 |
– |
int flags; |
51 |
– |
}; |
52 |
– |
|
53 |
– |
static struct entity targets[IRCD_BUFSIZE]; |
54 |
– |
static int ntargets, join_0; |
55 |
– |
|
56 |
– |
static int build_target_list(struct Client *, char *, char *); |
57 |
– |
static int is_target(struct Channel *); |
58 |
– |
|
46 |
|
static void m_join(struct Client *, struct Client *, int, char **); |
47 |
|
static void ms_join(struct Client *, struct Client *, int, char **); |
48 |
|
static void do_join_0(struct Client *client_p, struct Client *source_p); |
62 |
|
}; |
63 |
|
|
64 |
|
#ifndef STATIC_MODULES |
78 |
– |
|
65 |
|
void |
66 |
|
_modinit(void) |
67 |
|
{ |
77 |
|
const char *_version = "$Revision$"; |
78 |
|
#endif |
79 |
|
|
80 |
+ |
/* last0() stolen from ircu */ |
81 |
+ |
static char * |
82 |
+ |
last0(struct Client *client_p, struct Client *source_p, char *chanlist) |
83 |
+ |
{ |
84 |
+ |
char *p; |
85 |
+ |
int join0 = 0; |
86 |
+ |
|
87 |
+ |
for (p = chanlist; *p; ++p) /* find last "JOIN 0" */ |
88 |
+ |
{ |
89 |
+ |
if (*p == '0' && (*(p + 1) == ',' || *(p + 1) == '\0')) |
90 |
+ |
{ |
91 |
+ |
if ((*p + 1) == ',') |
92 |
+ |
++p; |
93 |
+ |
|
94 |
+ |
chanlist = p + 1; |
95 |
+ |
join0 = 1; |
96 |
+ |
} |
97 |
+ |
else |
98 |
+ |
{ |
99 |
+ |
while (*p != ',' && *p != '\0') /* skip past channel name */ |
100 |
+ |
++p; |
101 |
+ |
|
102 |
+ |
if (*p == '\0') /* hit the end */ |
103 |
+ |
break; |
104 |
+ |
} |
105 |
+ |
} |
106 |
+ |
|
107 |
+ |
if (join0) |
108 |
+ |
do_join_0(client_p, source_p); |
109 |
+ |
|
110 |
+ |
return chanlist; |
111 |
+ |
} |
112 |
+ |
|
113 |
|
/* m_join() |
114 |
|
* parv[0] = sender prefix |
115 |
|
* parv[1] = channel |
119 |
|
m_join(struct Client *client_p, struct Client *source_p, |
120 |
|
int parc, char *parv[]) |
121 |
|
{ |
122 |
+ |
char *p = NULL; |
123 |
+ |
char *key_list = NULL; |
124 |
+ |
char *chan_list = NULL; |
125 |
+ |
char *chan = NULL; |
126 |
|
struct Channel *chptr = NULL; |
127 |
< |
char *key = NULL; |
105 |
< |
int i, a; |
127 |
> |
int i = 0; |
128 |
|
unsigned int flags = 0; |
129 |
+ |
unsigned int error_reported = 0; |
130 |
|
|
131 |
|
if (*parv[1] == '\0') |
132 |
|
{ |
137 |
|
|
138 |
|
assert(client_p == source_p); |
139 |
|
|
140 |
< |
build_target_list(source_p, parv[1], parv[2]); |
141 |
< |
|
119 |
< |
if ((a = (join_0 >= 0) ? join_0 : 0)) |
120 |
< |
do_join_0(client_p, source_p); |
140 |
> |
key_list = parv[2]; |
141 |
> |
chan_list = last0(client_p, source_p, parv[1]); |
142 |
|
|
143 |
< |
for (; a < ntargets; a++) |
143 |
> |
for (chan = strtoken(&p, chan_list, ","); chan; |
144 |
> |
chan = strtoken(&p, NULL, ",")) |
145 |
|
{ |
146 |
< |
chptr = targets[a].chptr; |
125 |
< |
key = targets[a].key; |
126 |
< |
flags = targets[a].flags; |
146 |
> |
char *key = NULL; |
147 |
|
|
148 |
< |
if (IsMember(source_p, chptr)) |
148 |
> |
/* If we have any more keys, take the first for this channel. */ |
149 |
> |
if (!EmptyString(key_list) && (key_list = strchr(key = key_list, ','))) |
150 |
> |
*key_list++ = '\0'; |
151 |
> |
|
152 |
> |
/* Empty keys are the same as no keys. */ |
153 |
> |
if (key && *key == '\0') |
154 |
> |
key = NULL; |
155 |
> |
|
156 |
> |
if (!IsChanPrefix(*chan) || !check_channel_name(chan)) |
157 |
> |
{ |
158 |
> |
sendto_one(source_p, form_str(ERR_BADCHANNAME), |
159 |
> |
me.name, source_p->name, chan); |
160 |
> |
continue; |
161 |
> |
} |
162 |
> |
|
163 |
> |
if (ConfigChannel.disable_local_channels && (*chan == '&')) |
164 |
> |
{ |
165 |
> |
sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), |
166 |
> |
me.name, source_p->name, chan); |
167 |
> |
continue; |
168 |
> |
} |
169 |
> |
|
170 |
> |
if (strlen(chan) > LOCAL_CHANNELLEN) |
171 |
> |
{ |
172 |
> |
sendto_one(source_p, form_str(ERR_BADCHANNAME), |
173 |
> |
me.name, source_p->name, chan); |
174 |
|
continue; |
175 |
+ |
} |
176 |
+ |
|
177 |
+ |
if (!IsExemptResv(source_p) && |
178 |
+ |
!(IsOper(source_p) && ConfigFileEntry.oper_pass_resv) && |
179 |
+ |
(!hash_find_resv(chan) == ConfigChannel.restrict_channels)) |
180 |
+ |
{ |
181 |
+ |
sendto_one(source_p, form_str(ERR_BADCHANNAME), |
182 |
+ |
me.name, source_p->name, chan); |
183 |
+ |
sendto_realops_flags(UMODE_SPY, L_ALL, |
184 |
+ |
"User %s (%s@%s) is attempting to join locally juped channel %s", |
185 |
+ |
source_p->name, source_p->username, source_p->host, chan); |
186 |
+ |
continue; |
187 |
+ |
} |
188 |
+ |
|
189 |
+ |
if ((dlink_list_length(&source_p->channel) >= ConfigChannel.max_chans_per_user) && |
190 |
+ |
(!IsOper(source_p) || (dlink_list_length(&source_p->channel) >= |
191 |
+ |
ConfigChannel.max_chans_per_user * 3))) |
192 |
+ |
{ |
193 |
+ |
if (!error_reported++) |
194 |
+ |
sendto_one(source_p, form_str(ERR_TOOMANYCHANNELS), |
195 |
+ |
me.name, source_p->name, chan); |
196 |
+ |
continue; |
197 |
+ |
} |
198 |
+ |
|
199 |
+ |
if ((chptr = hash_find_channel(chan)) != NULL) |
200 |
+ |
{ |
201 |
+ |
if (IsMember(source_p, chptr)) |
202 |
+ |
continue; |
203 |
+ |
|
204 |
+ |
if (splitmode && !IsOper(source_p) && (*chan != '&') && |
205 |
+ |
ConfigChannel.no_join_on_split) |
206 |
+ |
{ |
207 |
+ |
sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), |
208 |
+ |
me.name, source_p->name, chan); |
209 |
+ |
continue; |
210 |
+ |
} |
211 |
+ |
|
212 |
+ |
/* |
213 |
+ |
* This should never be the case unless there is some sort of |
214 |
+ |
* persistant channels. |
215 |
+ |
*/ |
216 |
+ |
if (dlink_list_length(&chptr->members) == 0) |
217 |
+ |
flags = CHFL_CHANOP; |
218 |
+ |
else |
219 |
+ |
flags = 0; |
220 |
+ |
} |
221 |
+ |
else |
222 |
+ |
{ |
223 |
+ |
if (splitmode && !IsOper(source_p) && (*chan != '&') && |
224 |
+ |
(ConfigChannel.no_create_on_split || ConfigChannel.no_join_on_split)) |
225 |
+ |
{ |
226 |
+ |
sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), |
227 |
+ |
me.name, source_p->name, chan); |
228 |
+ |
continue; |
229 |
+ |
} |
230 |
+ |
|
231 |
+ |
flags = CHFL_CHANOP; |
232 |
+ |
|
233 |
+ |
if ((chptr = get_or_create_channel(source_p, chan, NULL)) == NULL) |
234 |
+ |
{ |
235 |
+ |
sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), |
236 |
+ |
me.name, source_p->name, chan); |
237 |
+ |
continue; |
238 |
+ |
} |
239 |
+ |
} |
240 |
|
|
241 |
|
if (!IsOper(source_p)) |
242 |
|
check_spambot_warning(source_p, chptr->chname); |
246 |
|
*/ |
247 |
|
if ((i = can_join(source_p, chptr, key))) |
248 |
|
{ |
249 |
< |
sendto_one(source_p, form_str(i), me.name, source_p->name, chptr->chname); |
249 |
> |
sendto_one(source_p, form_str(i), me.name, |
250 |
> |
source_p->name, chptr->chname); |
251 |
|
continue; |
252 |
|
} |
253 |
|
|
143 |
– |
/* add the user to the channel */ |
254 |
|
add_user_to_channel(chptr, source_p, flags, YES); |
255 |
|
|
256 |
|
/* |
257 |
< |
** Set timestamp if appropriate, and propagate |
258 |
< |
*/ |
257 |
> |
* Set timestamp if appropriate, and propagate |
258 |
> |
*/ |
259 |
|
if (flags & CHFL_CHANOP) |
260 |
|
{ |
261 |
|
chptr->channelts = CurrentTime; |
273 |
|
/* |
274 |
|
* notify all other users on the new channel |
275 |
|
*/ |
166 |
– |
/* XXX just exactly who is going to be =on= this new channel |
167 |
– |
* other than just the creator at this time? ? ? |
168 |
– |
*/ |
276 |
|
sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s!%s@%s JOIN :%s", |
277 |
|
source_p->name, source_p->username, |
278 |
|
source_p->host, chptr->chname); |
279 |
< |
|
173 |
< |
sendto_channel_local(ALL_MEMBERS, NO, chptr, |
174 |
< |
":%s MODE %s +nt", |
279 |
> |
sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s MODE %s +nt", |
280 |
|
me.name, chptr->chname); |
281 |
|
} |
282 |
|
else |
511 |
|
do_join_0(struct Client *client_p, struct Client *source_p) |
512 |
|
{ |
513 |
|
struct Channel *chptr = NULL; |
514 |
< |
dlink_node *ptr, *ptr_next; |
514 |
> |
dlink_node *ptr = NULL, *ptr_next = NULL; |
515 |
|
|
516 |
< |
if (source_p->channel.head != NULL && |
412 |
< |
MyConnect(source_p) && !IsOper(source_p)) |
516 |
> |
if (source_p->channel.head && MyConnect(source_p) && !IsOper(source_p)) |
517 |
|
check_spambot_warning(source_p, NULL); |
518 |
|
|
519 |
|
DLINK_FOREACH_SAFE(ptr, ptr_next, source_p->channel.head) |
520 |
|
{ |
521 |
|
chptr = ((struct Membership *)ptr->data)->chptr; |
522 |
|
|
523 |
< |
/* if the last occurance of this chan is before a 0, leave */ |
524 |
< |
if (is_target(chptr) < join_0) |
525 |
< |
{ |
526 |
< |
sendto_server(client_p, NULL, chptr, CAP_TS6, NOCAPS, NOFLAGS, |
527 |
< |
":%s PART %s", ID(source_p), chptr->chname); |
528 |
< |
sendto_server(client_p, NULL, chptr, NOCAPS, CAP_TS6, NOFLAGS, |
529 |
< |
":%s PART %s", source_p->name, chptr->chname); |
426 |
< |
sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s!%s@%s PART %s", |
427 |
< |
source_p->name, source_p->username, |
428 |
< |
source_p->host, chptr->chname); |
429 |
< |
remove_user_from_channel(ptr->data); |
430 |
< |
} |
431 |
< |
} |
432 |
< |
} |
433 |
< |
|
434 |
< |
/* build_target_list() |
435 |
< |
* |
436 |
< |
* inputs - pointer to given client_p (server) |
437 |
< |
* - pointer to given source (oper/client etc.) |
438 |
< |
* - pointer to list of channels |
439 |
< |
* - pointer to list of keys |
440 |
< |
* output - number of valid entities |
441 |
< |
* side effects - targets list is modified to contain a list of |
442 |
< |
* pointers to channels. display whatever errors |
443 |
< |
* that result from a join attempt to the user. |
444 |
< |
* |
445 |
< |
*/ |
446 |
< |
static int |
447 |
< |
build_target_list(struct Client *source_p, char *channels, char *keys) |
448 |
< |
{ |
449 |
< |
int error_reported = 0, flags = 0; |
450 |
< |
char *p = NULL, *p2 = NULL, *chan, *key = keys; |
451 |
< |
struct Channel *chptr = NULL; |
452 |
< |
|
453 |
< |
ntargets = 0; |
454 |
< |
join_0 = -1; |
455 |
< |
|
456 |
< |
key = key ? strtoken(&p2, keys, ",") : NULL; |
457 |
< |
|
458 |
< |
for (chan = strtoken(&p, channels, ","); chan; |
459 |
< |
key = key ? strtoken(&p2, NULL, ",") : NULL, |
460 |
< |
chan = strtoken(&p, NULL, ",")) |
461 |
< |
{ |
462 |
< |
if (!check_channel_name(chan)) |
463 |
< |
{ |
464 |
< |
sendto_one(source_p, form_str(ERR_BADCHANNAME), |
465 |
< |
me.name, source_p->name, chan); |
466 |
< |
continue; |
467 |
< |
} |
468 |
< |
|
469 |
< |
if (*chan == '0' && !atoi(chan)) |
470 |
< |
{ |
471 |
< |
targets[ntargets].chptr = NULL; |
472 |
< |
targets[ntargets].key = NULL; |
473 |
< |
targets[ntargets++].flags = 0; |
474 |
< |
|
475 |
< |
join_0 = ntargets; |
476 |
< |
continue; |
477 |
< |
} |
478 |
< |
else if (!IsChanPrefix(*chan)) |
479 |
< |
{ |
480 |
< |
sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), |
481 |
< |
me.name, source_p->name, chan); |
482 |
< |
continue; |
483 |
< |
} |
484 |
< |
|
485 |
< |
if (ConfigChannel.disable_local_channels && (*chan == '&')) |
486 |
< |
{ |
487 |
< |
sendto_one(source_p, form_str(ERR_NOSUCHCHANNEL), |
488 |
< |
me.name, source_p->name, chan); |
489 |
< |
continue; |
490 |
< |
} |
491 |
< |
|
492 |
< |
if (strlen(chan) > LOCAL_CHANNELLEN) |
493 |
< |
{ |
494 |
< |
sendto_one(source_p, form_str(ERR_BADCHANNAME), |
495 |
< |
me.name, source_p->name, chan); |
496 |
< |
continue; |
497 |
< |
} |
498 |
< |
|
499 |
< |
if (!IsExemptResv(source_p) && |
500 |
< |
!(IsOper(source_p) && ConfigFileEntry.oper_pass_resv) && |
501 |
< |
(!hash_find_resv(chan) == ConfigChannel.restrict_channels)) |
502 |
< |
{ |
503 |
< |
sendto_one(source_p, form_str(ERR_BADCHANNAME), |
504 |
< |
me.name, source_p->name, chan); |
505 |
< |
sendto_realops_flags(UMODE_SPY, L_ALL, |
506 |
< |
"User %s (%s@%s) is attempting to join locally juped channel %s", |
507 |
< |
source_p->name, source_p->username, source_p->host, chan); |
508 |
< |
continue; |
509 |
< |
} |
510 |
< |
|
511 |
< |
if ((dlink_list_length(&source_p->channel)+ntargets >= ConfigChannel.max_chans_per_user) && |
512 |
< |
(!IsOper(source_p) || (dlink_list_length(&source_p->channel)+ntargets >= |
513 |
< |
ConfigChannel.max_chans_per_user * 3))) |
514 |
< |
{ |
515 |
< |
if (!error_reported++) |
516 |
< |
sendto_one(source_p, form_str(ERR_TOOMANYCHANNELS), |
517 |
< |
me.name, source_p->name, chan); |
518 |
< |
continue; |
519 |
< |
} |
520 |
< |
|
521 |
< |
if ((chptr = hash_find_channel(chan)) != NULL) |
522 |
< |
{ |
523 |
< |
if (splitmode && !IsOper(source_p) && (*chan != '&') && |
524 |
< |
ConfigChannel.no_join_on_split) |
525 |
< |
{ |
526 |
< |
sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), |
527 |
< |
me.name, source_p->name, chan); |
528 |
< |
continue; |
529 |
< |
} |
530 |
< |
|
531 |
< |
if (dlink_list_length(&chptr->members) == 0) |
532 |
< |
flags = CHFL_CHANOP; |
533 |
< |
else |
534 |
< |
flags = 0; |
535 |
< |
} |
536 |
< |
else |
537 |
< |
{ |
538 |
< |
if (splitmode && !IsOper(source_p) && (*chan != '&') && |
539 |
< |
(ConfigChannel.no_create_on_split || ConfigChannel.no_join_on_split)) |
540 |
< |
{ |
541 |
< |
sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), |
542 |
< |
me.name, source_p->name, chan); |
543 |
< |
continue; |
544 |
< |
} |
545 |
< |
|
546 |
< |
flags = CHFL_CHANOP; |
547 |
< |
if (!ServerInfo.hub) |
548 |
< |
{ |
549 |
< |
if ((*chan != '&') && uplink && IsCapable(uplink, CAP_LL)) |
550 |
< |
{ |
551 |
< |
sendto_one(uplink, ":%s CBURST %s %s %s", |
552 |
< |
me.name, chan, source_p->name, key ? key : ""); |
553 |
< |
continue; |
554 |
< |
} |
555 |
< |
} |
556 |
< |
|
557 |
< |
if ((chptr = get_or_create_channel(source_p, chan, NULL)) == NULL) |
558 |
< |
{ |
559 |
< |
sendto_one(source_p, form_str(ERR_UNAVAILRESOURCE), |
560 |
< |
me.name, source_p->name, chan); |
561 |
< |
continue; |
562 |
< |
} |
563 |
< |
} |
564 |
< |
|
565 |
< |
if (is_target(chptr)) |
566 |
< |
continue; |
567 |
< |
|
568 |
< |
targets[ntargets].chptr = chptr; |
569 |
< |
targets[ntargets].key = key; |
570 |
< |
targets[ntargets++].flags = flags; |
571 |
< |
} |
572 |
< |
|
573 |
< |
return ((ntargets) ? 1 : 0); |
574 |
< |
} |
575 |
< |
|
576 |
< |
/* is_target() |
577 |
< |
* |
578 |
< |
* inputs - channel to check |
579 |
< |
* output - YES if duplicate pointer in table, NO if not. |
580 |
< |
* note, this does the canonize using pointers |
581 |
< |
* side effects - NONE |
582 |
< |
*/ |
583 |
< |
static int |
584 |
< |
is_target(struct Channel *chptr) |
585 |
< |
{ |
586 |
< |
int i; |
523 |
> |
sendto_server(client_p, NULL, chptr, CAP_TS6, NOCAPS, NOFLAGS, |
524 |
> |
":%s PART %s", ID(source_p), chptr->chname); |
525 |
> |
sendto_server(client_p, NULL, chptr, NOCAPS, CAP_TS6, NOFLAGS, |
526 |
> |
":%s PART %s", source_p->name, chptr->chname); |
527 |
> |
sendto_channel_local(ALL_MEMBERS, NO, chptr, ":%s!%s@%s PART %s", |
528 |
> |
source_p->name, source_p->username, |
529 |
> |
source_p->host, chptr->chname); |
530 |
|
|
531 |
< |
/* |
589 |
< |
* we step through this backwards for do_join_0()s sake. |
590 |
< |
* if the returned value is > join_0 (the highest 0 in the targets) |
591 |
< |
* we know they are supposed to stay in that channel. |
592 |
< |
*/ |
593 |
< |
for (i = ntargets-1; i >=0; i--) |
594 |
< |
{ |
595 |
< |
if (targets[i].chptr == chptr) |
596 |
< |
return i; |
531 |
> |
remove_user_from_channel(ptr->data); |
532 |
|
} |
598 |
– |
|
599 |
– |
return 0; |
533 |
|
} |
534 |
|
|
535 |
|
/* set_final_mode() |