1 |
/* |
2 |
* ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd) |
3 |
* |
4 |
* Copyright (c) 1997-2022 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 m_sjoin.c |
23 |
* \brief Includes required functions for processing the SJOIN command. |
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 "irc_string.h" |
35 |
#include "ircd.h" |
36 |
#include "numeric.h" |
37 |
#include "send.h" |
38 |
#include "parse.h" |
39 |
#include "modules.h" |
40 |
#include "server.h" |
41 |
#include "conf.h" |
42 |
#include "misc.h" |
43 |
|
44 |
|
45 |
/* set_final_mode |
46 |
* |
47 |
* inputs - channel mode |
48 |
* - old channel mode |
49 |
* output - NONE |
50 |
* side effects - walk through all the channel modes turning off modes |
51 |
* that were on in oldmode but aren't on in mode. |
52 |
* Then walk through turning on modes that are on in mode |
53 |
* but were not set in oldmode. |
54 |
*/ |
55 |
static void |
56 |
set_final_mode(const struct Mode *mode, const struct Mode *oldmode, char *mbuf, char *pbuf) |
57 |
{ |
58 |
int what = MODE_QUERY; |
59 |
|
60 |
for (const struct chan_mode *tab = cmode_tab; tab->letter; ++tab) |
61 |
{ |
62 |
if (tab->mode && (tab->mode & mode->mode) && !(tab->mode & oldmode->mode)) |
63 |
{ |
64 |
if (what != MODE_ADD) |
65 |
{ |
66 |
*mbuf++ = '+'; |
67 |
what = MODE_ADD; |
68 |
} |
69 |
|
70 |
*mbuf++ = tab->letter; |
71 |
} |
72 |
} |
73 |
|
74 |
for (const struct chan_mode *tab = cmode_tab; tab->letter; ++tab) |
75 |
{ |
76 |
if (tab->mode && (tab->mode & oldmode->mode) && !(tab->mode & mode->mode)) |
77 |
{ |
78 |
if (what != MODE_DEL) |
79 |
{ |
80 |
*mbuf++ = '-'; |
81 |
what = MODE_DEL; |
82 |
} |
83 |
|
84 |
*mbuf++ = tab->letter; |
85 |
} |
86 |
} |
87 |
|
88 |
if (oldmode->limit && mode->limit == 0) |
89 |
{ |
90 |
if (what != MODE_DEL) |
91 |
{ |
92 |
*mbuf++ = '-'; |
93 |
what = MODE_DEL; |
94 |
} |
95 |
|
96 |
*mbuf++ = 'l'; |
97 |
} |
98 |
|
99 |
if (oldmode->key[0] && mode->key[0] == '\0') |
100 |
{ |
101 |
if (what != MODE_DEL) |
102 |
{ |
103 |
*mbuf++ = '-'; |
104 |
what = MODE_DEL; |
105 |
} |
106 |
|
107 |
*mbuf++ = 'k'; |
108 |
pbuf += sprintf(pbuf, "%s ", oldmode->key); |
109 |
} |
110 |
|
111 |
if (mode->limit && oldmode->limit != mode->limit) |
112 |
{ |
113 |
if (what != MODE_ADD) |
114 |
{ |
115 |
*mbuf++ = '+'; |
116 |
what = MODE_ADD; |
117 |
} |
118 |
|
119 |
*mbuf++ = 'l'; |
120 |
pbuf += sprintf(pbuf, "%u ", mode->limit); |
121 |
} |
122 |
|
123 |
if (mode->key[0] && strcmp(oldmode->key, mode->key)) |
124 |
{ |
125 |
if (what != MODE_ADD) |
126 |
{ |
127 |
*mbuf++ = '+'; |
128 |
what = MODE_ADD; |
129 |
} |
130 |
|
131 |
*mbuf++ = 'k'; |
132 |
pbuf += sprintf(pbuf, "%s ", mode->key); |
133 |
} |
134 |
|
135 |
*mbuf = '\0'; |
136 |
} |
137 |
|
138 |
/* remove_ban_list() |
139 |
* |
140 |
* inputs - channel, source, list to remove, char of mode |
141 |
* outputs - none |
142 |
* side effects - given ban list is removed, modes are sent to local clients |
143 |
*/ |
144 |
static void |
145 |
remove_ban_list(struct Channel *channel, const struct Client *client, dlink_list *list, char c) |
146 |
{ |
147 |
char modebuf[IRCD_BUFSIZE]; |
148 |
char parabuf[IRCD_BUFSIZE]; |
149 |
char *mbuf; |
150 |
char *pbuf; |
151 |
int count = 0; |
152 |
int cur_len, mlen; |
153 |
|
154 |
if (dlink_list_length(list) == 0) |
155 |
return; |
156 |
|
157 |
cur_len = mlen = snprintf(modebuf, sizeof(modebuf), ":%s MODE %s -", |
158 |
client->name, channel->name); |
159 |
mbuf = modebuf + mlen; |
160 |
pbuf = parabuf; |
161 |
|
162 |
while (list->head) |
163 |
{ |
164 |
struct Ban *ban = list->head->data; |
165 |
int plen = ban->banstr_len + 2; /* +2 = b and space */ |
166 |
|
167 |
if (count >= MAXMODEPARAMS || |
168 |
(cur_len + 1 /* space between */ + (plen - 1)) > IRCD_BUFSIZE - 2) |
169 |
{ |
170 |
/* NUL-terminate and remove trailing space */ |
171 |
*mbuf = *(pbuf - 1) = '\0'; |
172 |
sendto_channel_local(NULL, channel, 0, 0, 0, "%s %s", modebuf, parabuf); |
173 |
|
174 |
cur_len = mlen; |
175 |
mbuf = modebuf + mlen; |
176 |
pbuf = parabuf; |
177 |
count = 0; |
178 |
} |
179 |
|
180 |
*mbuf++ = c; |
181 |
cur_len += plen; |
182 |
pbuf += sprintf(pbuf, "%s ", ban->banstr); |
183 |
++count; |
184 |
|
185 |
remove_ban(ban, list); |
186 |
} |
187 |
|
188 |
*mbuf = *(pbuf - 1) = '\0'; |
189 |
sendto_channel_local(NULL, channel, 0, 0, 0, "%s %s", modebuf, parabuf); |
190 |
} |
191 |
|
192 |
/* ms_sjoin() |
193 |
* |
194 |
* parv[0] - command |
195 |
* parv[1] - TS |
196 |
* parv[2] - channel |
197 |
* parv[3] - modes + n arguments (key and/or limit) |
198 |
* parv[4+n] - flags+nick list (all in one parameter) |
199 |
* |
200 |
* process a SJOIN, taking the TS's into account to either ignore the |
201 |
* incoming modes or undo the existing ones or merge them, and JOIN |
202 |
* all the specified users while sending JOIN/MODEs to local clients |
203 |
*/ |
204 |
static void |
205 |
ms_sjoin(struct Client *source_p, int parc, char *parv[]) |
206 |
{ |
207 |
struct Client *target_p = NULL; |
208 |
struct Mode mode = { .mode = 0, .limit = 0, .key[0] = '\0' }; |
209 |
int args = 0; |
210 |
bool isnew = false; |
211 |
bool keep_our_modes = true; |
212 |
bool keep_new_modes = true; |
213 |
bool have_many_uids = false; |
214 |
unsigned int lcount; |
215 |
char uid_prefix[CMEMBER_STATUS_FLAGS_LEN + 1]; |
216 |
char *up = NULL; |
217 |
int len_uid = 0; |
218 |
int buflen = 0; |
219 |
int slen; |
220 |
unsigned int fl; |
221 |
char *s; |
222 |
char *sptr; |
223 |
char uid_buf[IRCD_BUFSIZE]; /* buffer for modes/prefixes */ |
224 |
char *uid_ptr; |
225 |
char *p; /* pointer used making sjbuf */ |
226 |
const char *para[MAXMODEPARAMS]; |
227 |
char sendbuf[MODEBUFLEN] = ""; |
228 |
char modebuf[MODEBUFLEN] = ""; |
229 |
char parabuf[MODEBUFLEN] = ""; |
230 |
unsigned int pargs = 0; |
231 |
|
232 |
if (!IsServer(source_p)) |
233 |
return; |
234 |
|
235 |
if (channel_check_name(parv[2], false) == false) |
236 |
{ |
237 |
sendto_realops_flags(UMODE_DEBUG, L_ALL, SEND_NOTICE, |
238 |
"*** Too long or invalid channel name from %s(via %s): %s", |
239 |
source_p->name, source_p->from->name, parv[2]); |
240 |
return; |
241 |
} |
242 |
|
243 |
for (const char *modes = parv[3]; *modes; ++modes) |
244 |
{ |
245 |
switch (*modes) |
246 |
{ |
247 |
case 'k': |
248 |
strlcpy(mode.key, parv[4 + args], sizeof(mode.key)); |
249 |
++args; |
250 |
|
251 |
if (parc < 5 + args) |
252 |
return; |
253 |
break; |
254 |
|
255 |
case 'l': |
256 |
mode.limit = atoi(parv[4 + args]); |
257 |
++args; |
258 |
|
259 |
if (parc < 5 + args) |
260 |
return; |
261 |
break; |
262 |
|
263 |
default: |
264 |
{ |
265 |
const struct chan_mode *cmode = cmode_map[(unsigned char)*modes]; |
266 |
|
267 |
if (cmode) |
268 |
mode.mode |= cmode->mode; |
269 |
break; |
270 |
} |
271 |
} |
272 |
} |
273 |
|
274 |
struct Channel *channel = hash_find_channel(parv[2]); |
275 |
if (channel == NULL) |
276 |
{ |
277 |
isnew = true; |
278 |
channel = channel_make(parv[2]); |
279 |
} |
280 |
|
281 |
uintmax_t newts = strtoumax(parv[1], NULL, 10); |
282 |
uintmax_t oldts = channel->creation_time; |
283 |
|
284 |
if (newts == 0 && isnew == false && oldts) |
285 |
{ |
286 |
sendto_channel_local(NULL, channel, 0, 0, 0, |
287 |
":%s NOTICE %s :*** Notice -- TS for %s changed from %ju to 0", |
288 |
me.name, channel->name, channel->name, oldts); |
289 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ALL, SEND_NOTICE, |
290 |
"Server %s changing TS on %s from %ju to 0", |
291 |
source_p->name, channel->name, oldts); |
292 |
} |
293 |
|
294 |
if (isnew == true) |
295 |
channel->creation_time = newts; |
296 |
else if (newts == 0 || oldts == 0) |
297 |
channel->creation_time = 0; |
298 |
else if (newts == oldts) |
299 |
; |
300 |
else if (newts < oldts) |
301 |
{ |
302 |
keep_our_modes = false; |
303 |
channel->creation_time = newts; |
304 |
} |
305 |
else |
306 |
keep_new_modes = false; |
307 |
|
308 |
struct Mode *oldmode = &channel->mode; |
309 |
|
310 |
if (keep_new_modes == false) |
311 |
mode = *oldmode; |
312 |
else if (keep_our_modes == true) |
313 |
{ |
314 |
mode.mode |= oldmode->mode; |
315 |
|
316 |
if (oldmode->limit > mode.limit) |
317 |
mode.limit = oldmode->limit; |
318 |
if (strcmp(mode.key, oldmode->key) < 0) |
319 |
strlcpy(mode.key, oldmode->key, sizeof(mode.key)); |
320 |
} |
321 |
|
322 |
set_final_mode(&mode, oldmode, modebuf, parabuf); |
323 |
channel->mode = mode; |
324 |
|
325 |
const struct Client *origin = source_p; |
326 |
if (IsHidden(source_p) || ConfigServerHide.hide_servers) |
327 |
origin = &me; |
328 |
|
329 |
/* Lost the TS, other side wins, so remove modes on this side */ |
330 |
if (keep_our_modes == false) |
331 |
{ |
332 |
/* Update channel name to be the correct case */ |
333 |
strlcpy(channel->name, parv[2], sizeof(channel->name)); |
334 |
|
335 |
channel_demote_members(channel, origin, CHFL_CHANOP, 'o'); |
336 |
channel_demote_members(channel, origin, CHFL_HALFOP, 'h'); |
337 |
channel_demote_members(channel, origin, CHFL_VOICE, 'v'); |
338 |
|
339 |
remove_ban_list(channel, origin, &channel->banlist, 'b'); |
340 |
remove_ban_list(channel, origin, &channel->exceptlist, 'e'); |
341 |
remove_ban_list(channel, origin, &channel->invexlist, 'I'); |
342 |
|
343 |
clear_ban_cache_list(&channel->members_local); |
344 |
invite_clear_list(&channel->invites); |
345 |
|
346 |
if (channel->topic[0]) |
347 |
{ |
348 |
channel_set_topic(channel, "", "", 0, false); |
349 |
sendto_channel_local(NULL, channel, 0, 0, 0, ":%s TOPIC %s :", |
350 |
origin->name, channel->name); |
351 |
} |
352 |
|
353 |
sendto_channel_local(NULL, channel, 0, 0, 0, |
354 |
":%s NOTICE %s :*** Notice -- TS for %s changed from %ju to %ju", |
355 |
me.name, channel->name, channel->name, |
356 |
oldts, newts); |
357 |
} |
358 |
|
359 |
if (*modebuf) |
360 |
sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s %s", |
361 |
origin->name, channel->name, modebuf, parabuf); |
362 |
|
363 |
if (*parv[3] != '0' && keep_new_modes == true) |
364 |
channel_modes(channel, source_p, NULL, modebuf, parabuf); |
365 |
else |
366 |
{ |
367 |
modebuf[0] = '0'; |
368 |
modebuf[1] = '\0'; |
369 |
} |
370 |
|
371 |
buflen = snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %ju %s %s %s:", |
372 |
source_p->id, channel->creation_time, |
373 |
channel->name, modebuf, parabuf); |
374 |
uid_ptr = uid_buf + buflen; |
375 |
|
376 |
/* |
377 |
* Check we can fit a nick on the end, as well as \r\n and a prefix " |
378 |
* @%+", and a space. |
379 |
*/ |
380 |
if (buflen >= (IRCD_BUFSIZE - IDLEN - 2 - CMEMBER_STATUS_FLAGS_LEN - 1)) |
381 |
{ |
382 |
sendto_realops_flags(UMODE_SERVNOTICE, L_ALL, SEND_NOTICE, |
383 |
"Long SJOIN from server: %s(via %s) (ignored)", |
384 |
source_p->name, source_p->from->name); |
385 |
return; |
386 |
} |
387 |
|
388 |
char *mbuf = modebuf; |
389 |
*mbuf++ = '+'; |
390 |
|
391 |
s = parv[args + 4]; |
392 |
while (*s == ' ') |
393 |
++s; |
394 |
|
395 |
if ((p = strchr(s, ' '))) |
396 |
{ |
397 |
*p++ = '\0'; |
398 |
|
399 |
while (*p == ' ') |
400 |
++p; |
401 |
have_many_uids = *p != '\0'; |
402 |
} |
403 |
|
404 |
while (*s) |
405 |
{ |
406 |
bool valid_mode = true; |
407 |
fl = 0; |
408 |
|
409 |
do |
410 |
{ |
411 |
switch (*s) |
412 |
{ |
413 |
case '@': |
414 |
fl |= CHFL_CHANOP; |
415 |
++s; |
416 |
break; |
417 |
case '%': |
418 |
fl |= CHFL_HALFOP; |
419 |
++s; |
420 |
break; |
421 |
case '+': |
422 |
fl |= CHFL_VOICE; |
423 |
++s; |
424 |
break; |
425 |
default: |
426 |
valid_mode = false; |
427 |
break; |
428 |
} |
429 |
} while (valid_mode == true); |
430 |
|
431 |
/* |
432 |
* If the client doesn't exist, or if it's fake direction/server, skip. |
433 |
* we cannot send ERR_NOSUCHNICK here because if it's a UID, we cannot |
434 |
* lookup the nick, and it's better to never send the numeric than only |
435 |
* sometimes. |
436 |
*/ |
437 |
if ((target_p = find_person(source_p, s)) == NULL || target_p->from != source_p->from) |
438 |
goto nextnick; |
439 |
|
440 |
len_uid = strlen(target_p->id); |
441 |
up = uid_prefix; |
442 |
|
443 |
if (keep_new_modes == true) |
444 |
{ |
445 |
if (fl & CHFL_CHANOP) |
446 |
{ |
447 |
*up++ = '@'; |
448 |
len_uid++; |
449 |
} |
450 |
|
451 |
if (fl & CHFL_HALFOP) |
452 |
{ |
453 |
*up++ = '%'; |
454 |
len_uid++; |
455 |
} |
456 |
|
457 |
if (fl & CHFL_VOICE) |
458 |
{ |
459 |
*up++ = '+'; |
460 |
len_uid++; |
461 |
} |
462 |
} |
463 |
else |
464 |
fl = 0; |
465 |
|
466 |
*up = '\0'; |
467 |
|
468 |
if ((uid_ptr - uid_buf + len_uid) > (IRCD_BUFSIZE - 2)) |
469 |
{ |
470 |
sendto_server(source_p, 0, 0, "%s", uid_buf); |
471 |
|
472 |
buflen = snprintf(uid_buf, sizeof(uid_buf), ":%s SJOIN %ju %s %s %s:", |
473 |
source_p->id, channel->creation_time, |
474 |
channel->name, modebuf, parabuf); |
475 |
uid_ptr = uid_buf + buflen; |
476 |
} |
477 |
|
478 |
uid_ptr += sprintf(uid_ptr, "%s%s ", uid_prefix, target_p->id); |
479 |
|
480 |
if (member_find_link(target_p, channel) == NULL) |
481 |
{ |
482 |
add_user_to_channel(channel, target_p, fl, have_many_uids == false); |
483 |
|
484 |
sendto_channel_local(NULL, channel, 0, CAP_EXTENDED_JOIN, 0, ":%s!%s@%s JOIN %s %s :%s", |
485 |
target_p->name, target_p->username, |
486 |
target_p->host, channel->name, target_p->account, target_p->info); |
487 |
sendto_channel_local(NULL, channel, 0, 0, CAP_EXTENDED_JOIN, ":%s!%s@%s JOIN :%s", |
488 |
target_p->name, target_p->username, |
489 |
target_p->host, channel->name); |
490 |
|
491 |
if (target_p->away[0]) |
492 |
sendto_channel_local(target_p, channel, 0, CAP_AWAY_NOTIFY, 0, |
493 |
":%s!%s@%s AWAY :%s", |
494 |
target_p->name, target_p->username, |
495 |
target_p->host, target_p->away); |
496 |
} |
497 |
|
498 |
if (fl & CHFL_CHANOP) |
499 |
{ |
500 |
*mbuf++ = 'o'; |
501 |
para[pargs++] = target_p->name; |
502 |
|
503 |
if (pargs >= MAXMODEPARAMS) |
504 |
{ |
505 |
/* |
506 |
* Ok, the code is now going to "walk" through |
507 |
* sendbuf, filling in para strings. So, I will use sptr |
508 |
* to point into the sendbuf. |
509 |
* Notice, that ircsprintf() returns the number of chars |
510 |
* successfully inserted into string. |
511 |
* - Dianora |
512 |
*/ |
513 |
|
514 |
sptr = sendbuf; |
515 |
*mbuf = '\0'; |
516 |
|
517 |
for (lcount = 0; lcount < MAXMODEPARAMS; ++lcount) |
518 |
{ |
519 |
slen = sprintf(sptr, " %s", para[lcount]); /* see? */ |
520 |
sptr += slen; /* ready for next */ |
521 |
} |
522 |
|
523 |
sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s%s", |
524 |
origin->name, channel->name, modebuf, sendbuf); |
525 |
mbuf = modebuf; |
526 |
*mbuf++ = '+'; |
527 |
|
528 |
sendbuf[0] = '\0'; |
529 |
pargs = 0; |
530 |
} |
531 |
} |
532 |
|
533 |
if (fl & CHFL_HALFOP) |
534 |
{ |
535 |
*mbuf++ = 'h'; |
536 |
para[pargs++] = target_p->name; |
537 |
|
538 |
if (pargs >= MAXMODEPARAMS) |
539 |
{ |
540 |
sptr = sendbuf; |
541 |
*mbuf = '\0'; |
542 |
|
543 |
for (lcount = 0; lcount < MAXMODEPARAMS; ++lcount) |
544 |
{ |
545 |
slen = sprintf(sptr, " %s", para[lcount]); |
546 |
sptr += slen; |
547 |
} |
548 |
|
549 |
sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s%s", |
550 |
origin->name, channel->name, modebuf, sendbuf); |
551 |
|
552 |
mbuf = modebuf; |
553 |
*mbuf++ = '+'; |
554 |
|
555 |
sendbuf[0] = '\0'; |
556 |
pargs = 0; |
557 |
} |
558 |
} |
559 |
|
560 |
if (fl & CHFL_VOICE) |
561 |
{ |
562 |
*mbuf++ = 'v'; |
563 |
para[pargs++] = target_p->name; |
564 |
|
565 |
if (pargs >= MAXMODEPARAMS) |
566 |
{ |
567 |
sptr = sendbuf; |
568 |
*mbuf = '\0'; |
569 |
|
570 |
for (lcount = 0; lcount < MAXMODEPARAMS; ++lcount) |
571 |
{ |
572 |
slen = sprintf(sptr, " %s", para[lcount]); |
573 |
sptr += slen; |
574 |
} |
575 |
|
576 |
sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s%s", |
577 |
origin->name, channel->name, modebuf, sendbuf); |
578 |
|
579 |
mbuf = modebuf; |
580 |
*mbuf++ = '+'; |
581 |
|
582 |
sendbuf[0] = '\0'; |
583 |
pargs = 0; |
584 |
} |
585 |
} |
586 |
|
587 |
nextnick: |
588 |
if ((s = p) == NULL) |
589 |
break; |
590 |
|
591 |
while (*s == ' ') |
592 |
++s; |
593 |
|
594 |
if ((p = strchr(s, ' '))) |
595 |
{ |
596 |
*p++ = '\0'; |
597 |
|
598 |
while (*p == ' ') |
599 |
++p; |
600 |
} |
601 |
} |
602 |
|
603 |
*mbuf = '\0'; |
604 |
*(uid_ptr - 1) = '\0'; |
605 |
|
606 |
/* |
607 |
* checking for lcount < MAXMODEPARAMS at this time is wrong |
608 |
* since the code has already verified above that pargs < MAXMODEPARAMS |
609 |
* checking for para[lcount] != '\0' is also wrong, since |
610 |
* there is no place where para[lcount] is set! |
611 |
* - Dianora |
612 |
*/ |
613 |
|
614 |
if (pargs) |
615 |
{ |
616 |
sptr = sendbuf; |
617 |
|
618 |
for (lcount = 0; lcount < pargs; ++lcount) |
619 |
{ |
620 |
slen = sprintf(sptr, " %s", para[lcount]); |
621 |
sptr += slen; |
622 |
} |
623 |
|
624 |
sendto_channel_local(NULL, channel, 0, 0, 0, ":%s MODE %s %s%s", |
625 |
origin->name, channel->name, modebuf, sendbuf); |
626 |
} |
627 |
|
628 |
/* |
629 |
* If this happens, it's the result of a malformed SJOIN |
630 |
* a remnant from the old persistent channel code. *sigh* |
631 |
* Or it could be the result of a client just leaving |
632 |
* and leaving us with a channel formed just as the client parts. |
633 |
* - Dianora |
634 |
*/ |
635 |
if (dlink_list_length(&channel->members) == 0 && isnew == true) |
636 |
{ |
637 |
channel_free(channel); |
638 |
return; |
639 |
} |
640 |
|
641 |
if (*parv[4 + args] == '\0') |
642 |
return; |
643 |
|
644 |
sendto_server(source_p, 0, 0, "%s", uid_buf); |
645 |
} |
646 |
|
647 |
static struct Message sjoin_msgtab = |
648 |
{ |
649 |
.cmd = "SJOIN", |
650 |
.handlers[UNREGISTERED_HANDLER] = { .handler = m_unregistered }, |
651 |
.handlers[CLIENT_HANDLER] = { .handler = m_ignore }, |
652 |
.handlers[SERVER_HANDLER] = { .handler = ms_sjoin, .args_min = 5, .empty_last_arg = true }, |
653 |
.handlers[ENCAP_HANDLER] = { .handler = m_ignore }, |
654 |
.handlers[OPER_HANDLER] = { .handler = m_ignore } |
655 |
}; |
656 |
|
657 |
static void |
658 |
module_init(void) |
659 |
{ |
660 |
mod_add_cmd(&sjoin_msgtab); |
661 |
} |
662 |
|
663 |
static void |
664 |
module_exit(void) |
665 |
{ |
666 |
mod_del_cmd(&sjoin_msgtab); |
667 |
} |
668 |
|
669 |
struct module module_entry = |
670 |
{ |
671 |
.version = "$Revision$", |
672 |
.modinit = module_init, |
673 |
.modexit = module_exit, |
674 |
.is_core = true |
675 |
}; |