Initial import
[ratbox-ambernet.git] / modules / core / m_sjoin.c
blobda41ad24ec7a173024aa309cff96c404533fedaa
1 /*
2 * ircd-ratbox: A slightly useful ircd.
3 * m_sjoin.c: Joins a user to a channel.
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
24 * $Id: m_sjoin.c 22848 2006-07-05 13:35:49Z jilles $
27 #include "stdinc.h"
28 #include "tools.h"
29 #include "channel.h"
30 #include "client.h"
31 #include "hash.h"
32 #include "irc_string.h"
33 #include "sprintf_irc.h"
34 #include "ircd.h"
35 #include "numeric.h"
36 #include "send.h"
37 #include "common.h"
38 #include "msg.h"
39 #include "parse.h"
40 #include "modules.h"
41 #include "s_serv.h"
42 #include "s_conf.h"
44 static int ms_sjoin(struct Client *, struct Client *, int, const char **);
46 struct Message sjoin_msgtab = {
47 "SJOIN", 0, 0, 0, MFLG_SLOW,
48 {mg_unreg, mg_ignore, mg_ignore, {ms_sjoin, 0}, mg_ignore, mg_ignore}
51 mapi_clist_av1 sjoin_clist[] = { &sjoin_msgtab, NULL };
52 DECLARE_MODULE_AV1(sjoin, NULL, NULL, sjoin_clist, NULL, NULL, "$Revision: 22848 $");
55 * ms_sjoin
56 * parv[0] - sender
57 * parv[1] - TS
58 * parv[2] - channel
59 * parv[3] - modes + n arguments (key and/or limit)
60 * parv[4+n] - flags+nick list (all in one parameter)
62 * process a SJOIN, taking the TS's into account to either ignore the
63 * incoming modes or undo the existing ones or merge them, and JOIN
64 * all the specified users while sending JOIN/MODEs to local clients
68 static char modebuf[MODEBUFLEN];
69 static char parabuf[MODEBUFLEN];
70 static const char *para[MAXMODEPARAMS];
71 static char *mbuf;
72 static int pargs;
74 static void set_final_mode(struct Mode *mode, struct Mode *oldmode);
75 static void remove_our_modes(struct Channel *chptr, struct Client *source_p);
76 static void remove_ban_list(struct Channel *chptr, struct Client *source_p,
77 dlink_list *list, char c, int cap, int mems);
79 static int
80 ms_sjoin(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
82 static char buf_nick[BUFSIZE];
83 static char buf_uid[BUFSIZE];
84 static const char empty_modes[] = "0";
85 struct Channel *chptr;
86 struct Client *target_p;
87 time_t newts;
88 time_t oldts;
89 static struct Mode mode, *oldmode;
90 const char *modes;
91 int args = 0;
92 int keep_our_modes = 1;
93 int keep_new_modes = 1;
94 int fl;
95 int isnew;
96 int mlen_nick, mlen_uid;
97 int len_nick;
98 int len_uid;
99 int len;
100 int joins = 0;
101 const char *s;
102 char *ptr_nick;
103 char *ptr_uid;
104 char *p;
105 int i;
106 static char empty[] = "";
108 /* I dont trust servers *not* to end up sending us a blank sjoin, so
109 * its better not to make a big deal about it. --fl
111 if(parc < 5 || EmptyString(parv[4]))
112 return 0;
114 if(!IsChannelName(parv[2]) || !check_channel_name(parv[2]))
115 return 0;
117 /* SJOIN's for local channels can't happen. */
118 if(*parv[2] == '&')
119 return 0;
121 modebuf[0] = parabuf[0] = mode.key[0] = '\0';
122 pargs = mode.mode = mode.limit = 0;
124 mbuf = modebuf;
125 newts = atol(parv[1]);
127 s = parv[3];
128 while (*s)
130 switch (*(s++))
132 case 'i':
133 mode.mode |= MODE_INVITEONLY;
134 break;
135 case 'n':
136 mode.mode |= MODE_NOPRIVMSGS;
137 break;
138 case 'p':
139 mode.mode |= MODE_PRIVATE;
140 break;
141 case 's':
142 mode.mode |= MODE_SECRET;
143 break;
144 case 'm':
145 mode.mode |= MODE_MODERATED;
146 break;
147 case 't':
148 mode.mode |= MODE_TOPICLIMIT;
149 break;
150 #ifdef ENABLE_SERVICES
151 case 'r':
152 mode.mode |= MODE_REGONLY;
153 break;
154 #endif
155 case 'k':
156 strlcpy(mode.key, parv[4 + args], sizeof(mode.key));
157 args++;
158 if(parc < 5 + args)
159 return 0;
160 break;
161 case 'l':
162 mode.limit = atoi(parv[4 + args]);
163 args++;
164 if(parc < 5 + args)
165 return 0;
166 break;
170 s = parv[args + 4];
172 /* remove any leading spaces */
173 while (*s == ' ')
174 s++;
176 if(EmptyString(s))
177 return 0;
179 if((chptr = get_or_create_channel(source_p, parv[2], &isnew)) == NULL)
180 return 0; /* channel name too long? */
183 oldts = chptr->channelts;
184 oldmode = &chptr->mode;
186 if(!isnew && !newts && oldts)
188 sendto_channel_local(ALL_MEMBERS, chptr,
189 ":%s NOTICE %s :*** Notice -- TS for %s "
190 "changed from %ld to 0",
191 me.name, chptr->chname, chptr->chname, (long) oldts);
192 sendto_realops_flags(UMODE_ALL, L_ALL,
193 "Server %s changing TS on %s from %ld to 0",
194 source_p->name, chptr->chname, (long) oldts);
197 if(isnew)
198 chptr->channelts = newts;
199 else if(newts == 0 || oldts == 0)
200 chptr->channelts = 0;
201 else if(newts == oldts)
203 else if(newts < oldts)
205 keep_our_modes = NO;
206 chptr->channelts = newts;
208 else
209 keep_new_modes = NO;
211 if(!keep_new_modes)
212 mode = *oldmode;
213 else if(keep_our_modes)
215 mode.mode |= oldmode->mode;
216 if(oldmode->limit > mode.limit)
217 mode.limit = oldmode->limit;
218 if(strcmp(mode.key, oldmode->key) < 0)
219 strcpy(mode.key, oldmode->key);
222 set_final_mode(&mode, oldmode);
223 chptr->mode = mode;
225 /* Lost the TS, other side wins, so remove modes on this side */
226 if(!keep_our_modes)
228 remove_our_modes(chptr, source_p);
229 sendto_channel_local(ALL_MEMBERS, chptr,
230 ":%s NOTICE %s :*** Notice -- TS for %s changed from %ld to %ld",
231 me.name, chptr->chname, chptr->chname,
232 (long) oldts, (long) newts);
235 if(*modebuf != '\0')
236 sendto_channel_local(ALL_MEMBERS, chptr, ":%s MODE %s %s %s",
237 source_p->name, chptr->chname,
238 modebuf, parabuf);
240 *modebuf = *parabuf = '\0';
242 if(parv[3][0] != '0' && keep_new_modes)
243 modes = channel_modes(chptr, source_p);
244 else
245 modes = empty_modes;
247 mlen_nick = ircsprintf(buf_nick, ":%s SJOIN %ld %s %s :",
248 source_p->name, (long) chptr->channelts,
249 parv[2], modes);
250 ptr_nick = buf_nick + mlen_nick;
252 /* working on the presumption eventually itll be more efficient to
253 * build a TS6 buffer without checking its needed..
255 mlen_uid = ircsprintf(buf_uid, ":%s SJOIN %ld %s %s :",
256 use_id(source_p), (long) chptr->channelts,
257 parv[2], modes);
258 ptr_uid = buf_uid + mlen_uid;
260 mbuf = modebuf;
261 para[0] = para[1] = para[2] = para[3] = empty;
262 pargs = 0;
263 len_nick = len_uid = 0;
265 *mbuf++ = '+';
267 /* if theres a space, theres going to be more than one nick, change the
268 * first space to \0, so s is just the first nick, and point p to the
269 * second nick
271 if((p = strchr(s, ' ')) != NULL)
273 *p++ = '\0';
276 while (s)
278 fl = 0;
280 for (i = 0; i < 2; i++)
282 if(*s == '@')
284 fl |= CHFL_CHANOP;
285 s++;
287 else if(*s == '+')
289 fl |= CHFL_VOICE;
290 s++;
294 /* if the client doesnt exist or is fake direction, skip. */
295 if(!(target_p = find_client(s)) ||
296 (target_p->from != client_p) || !IsPerson(target_p))
297 goto nextnick;
299 /* we assume for these we can fit at least one nick/uid in.. */
301 /* check we can fit another status+nick+space into a buffer */
302 if((mlen_nick + len_nick + NICKLEN + 3) > (BUFSIZE - 3))
304 *(ptr_nick - 1) = '\0';
305 sendto_server(client_p->from, NULL, NOCAPS, CAP_TS6,
306 "%s", buf_nick);
307 ptr_nick = buf_nick + mlen_nick;
308 len_nick = 0;
311 if((mlen_uid + len_uid + IDLEN + 3) > (BUFSIZE - 3))
313 *(ptr_uid - 1) = '\0';
314 sendto_server(client_p->from, NULL, CAP_TS6, NOCAPS,
315 "%s", buf_uid);
316 ptr_uid = buf_uid + mlen_uid;
317 len_uid = 0;
320 if(keep_new_modes)
322 if(fl & CHFL_CHANOP)
324 *ptr_nick++ = '@';
325 *ptr_uid++ = '@';
326 len_nick++;
327 len_uid++;
329 if(fl & CHFL_VOICE)
331 *ptr_nick++ = '+';
332 *ptr_uid++ = '+';
333 len_nick++;
334 len_uid++;
338 /* copy the nick to the two buffers */
339 len = ircsprintf(ptr_nick, "%s ", target_p->name);
340 ptr_nick += len;
341 len_nick += len;
342 len = ircsprintf(ptr_uid, "%s ", use_id(target_p));
343 ptr_uid += len;
344 len_uid += len;
346 if(!keep_new_modes)
348 if(fl & CHFL_CHANOP)
349 fl = CHFL_DEOPPED;
350 else
351 fl = 0;
354 if(!IsMember(target_p, chptr))
356 add_user_to_channel(chptr, target_p, fl);
357 sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s JOIN :%s",
358 target_p->name,
359 target_p->username, target_p->host, parv[2]);
360 joins++;
363 if(fl & CHFL_CHANOP)
365 *mbuf++ = 'o';
366 para[pargs++] = target_p->name;
368 /* a +ov user.. bleh */
369 if(fl & CHFL_VOICE)
371 /* its possible the +o has filled up MAXMODEPARAMS, if so, start
372 * a new buffer
374 if(pargs >= MAXMODEPARAMS)
376 *mbuf = '\0';
377 sendto_channel_local(ALL_MEMBERS, chptr,
378 ":%s MODE %s %s %s %s %s %s",
379 source_p->name, chptr->chname,
380 modebuf,
381 para[0], para[1], para[2], para[3]);
382 mbuf = modebuf;
383 *mbuf++ = '+';
384 para[0] = para[1] = para[2] = para[3] = NULL;
385 pargs = 0;
388 *mbuf++ = 'v';
389 para[pargs++] = target_p->name;
392 else if(fl & CHFL_VOICE)
394 *mbuf++ = 'v';
395 para[pargs++] = target_p->name;
398 if(pargs >= MAXMODEPARAMS)
400 *mbuf = '\0';
401 sendto_channel_local(ALL_MEMBERS, chptr,
402 ":%s MODE %s %s %s %s %s %s",
403 source_p->name,
404 chptr->chname,
405 modebuf, para[0], para[1], para[2], para[3]);
406 mbuf = modebuf;
407 *mbuf++ = '+';
408 para[0] = para[1] = para[2] = para[3] = NULL;
409 pargs = 0;
412 nextnick:
413 /* p points to the next nick */
414 s = p;
416 /* if there was a trailing space and p was pointing to it, then we
417 * need to exit.. this has the side effect of breaking double spaces
418 * in an sjoin.. but that shouldnt happen anyway
420 if(s && (*s == '\0'))
421 s = p = NULL;
423 /* if p was NULL due to no spaces, s wont exist due to the above, so
424 * we cant check it for spaces.. if there are no spaces, then when
425 * we next get here, s will be NULL
427 if(s && ((p = strchr(s, ' ')) != NULL))
429 *p++ = '\0';
433 *mbuf = '\0';
434 if(pargs)
436 sendto_channel_local(ALL_MEMBERS, chptr,
437 ":%s MODE %s %s %s %s %s %s",
438 source_p->name, chptr->chname, modebuf,
439 para[0], CheckEmpty(para[1]), CheckEmpty(para[2]), CheckEmpty(para[3]));
442 if(!joins)
444 if(isnew)
445 destroy_channel(chptr);
447 return 0;
450 *(ptr_nick - 1) = '\0';
451 *(ptr_uid - 1) = '\0';
453 sendto_server(client_p->from, NULL, CAP_TS6, NOCAPS,
454 "%s", buf_uid);
455 sendto_server(client_p->from, NULL, NOCAPS, CAP_TS6,
456 "%s", buf_nick);
458 /* if the source does TS6 we have to remove our bans. Its now safe
459 * to issue -b's to the non-ts6 servers, as the sjoin we've just
460 * sent will kill any ops they have.
462 if(!keep_our_modes && source_p->id[0] != '\0')
464 if(dlink_list_length(&chptr->banlist) > 0)
465 remove_ban_list(chptr, source_p, &chptr->banlist,
466 'b', NOCAPS, ALL_MEMBERS);
468 if(dlink_list_length(&chptr->exceptlist) > 0)
469 remove_ban_list(chptr, source_p, &chptr->exceptlist,
470 'e', CAP_EX, ONLY_CHANOPS);
472 if(dlink_list_length(&chptr->invexlist) > 0)
473 remove_ban_list(chptr, source_p, &chptr->invexlist,
474 'I', CAP_IE, ONLY_CHANOPS);
476 chptr->bants++;
480 return 0;
483 struct mode_letter
485 int mode;
486 char letter;
489 static struct mode_letter flags[] = {
490 {MODE_NOPRIVMSGS, 'n'},
491 {MODE_TOPICLIMIT, 't'},
492 {MODE_SECRET, 's'},
493 {MODE_MODERATED, 'm'},
494 {MODE_INVITEONLY, 'i'},
495 {MODE_PRIVATE, 'p'},
496 #ifdef ENABLE_SERVICES
497 {MODE_REGONLY, 'r'},
498 #endif
499 {0, 0}
502 static void
503 set_final_mode(struct Mode *mode, struct Mode *oldmode)
505 int dir = MODE_QUERY;
506 char *pbuf = parabuf;
507 int len;
508 int i;
510 /* ok, first get a list of modes we need to add */
511 for (i = 0; flags[i].letter; i++)
513 if((mode->mode & flags[i].mode) && !(oldmode->mode & flags[i].mode))
515 if(dir != MODE_ADD)
517 *mbuf++ = '+';
518 dir = MODE_ADD;
520 *mbuf++ = flags[i].letter;
524 /* now the ones we need to remove. */
525 for (i = 0; flags[i].letter; i++)
527 if((oldmode->mode & flags[i].mode) && !(mode->mode & flags[i].mode))
529 if(dir != MODE_DEL)
531 *mbuf++ = '-';
532 dir = MODE_DEL;
534 *mbuf++ = flags[i].letter;
538 if(oldmode->limit && !mode->limit)
540 if(dir != MODE_DEL)
542 *mbuf++ = '-';
543 dir = MODE_DEL;
545 *mbuf++ = 'l';
547 if(oldmode->key[0] && !mode->key[0])
549 if(dir != MODE_DEL)
551 *mbuf++ = '-';
552 dir = MODE_DEL;
554 *mbuf++ = 'k';
555 len = ircsprintf(pbuf, "%s ", oldmode->key);
556 pbuf += len;
557 pargs++;
559 if(mode->limit && oldmode->limit != mode->limit)
561 if(dir != MODE_ADD)
563 *mbuf++ = '+';
564 dir = MODE_ADD;
566 *mbuf++ = 'l';
567 len = ircsprintf(pbuf, "%d ", mode->limit);
568 pbuf += len;
569 pargs++;
571 if(mode->key[0] && strcmp(oldmode->key, mode->key))
573 if(dir != MODE_ADD)
575 *mbuf++ = '+';
576 dir = MODE_ADD;
578 *mbuf++ = 'k';
579 len = ircsprintf(pbuf, "%s ", mode->key);
580 pbuf += len;
581 pargs++;
583 *mbuf = '\0';
587 * remove_our_modes
589 * inputs -
590 * output -
591 * side effects -
593 static void
594 remove_our_modes(struct Channel *chptr, struct Client *source_p)
596 struct membership *msptr;
597 dlink_node *ptr;
598 char lmodebuf[MODEBUFLEN];
599 const char *lpara[MAXMODEPARAMS];
600 int count = 0;
601 int i;
603 mbuf = lmodebuf;
604 *mbuf++ = '-';
606 for(i = 0; i < MAXMODEPARAMS; i++)
607 lpara[i] = NULL;
609 DLINK_FOREACH(ptr, chptr->members.head)
611 msptr = ptr->data;
613 if(is_chanop(msptr))
615 msptr->flags &= ~CHFL_CHANOP;
616 lpara[count++] = msptr->client_p->name;
617 *mbuf++ = 'o';
619 /* +ov, might not fit so check. */
620 if(is_voiced(msptr))
622 if(count >= MAXMODEPARAMS)
624 *mbuf = '\0';
625 sendto_channel_local(ALL_MEMBERS, chptr,
626 ":%s MODE %s %s %s %s %s %s",
627 me.name, chptr->chname,
628 lmodebuf, lpara[0], lpara[1],
629 lpara[2], lpara[3]);
631 /* preserve the initial '-' */
632 mbuf = lmodebuf;
633 *mbuf++ = '-';
634 count = 0;
636 for(i = 0; i < MAXMODEPARAMS; i++)
637 lpara[i] = NULL;
640 msptr->flags &= ~CHFL_VOICE;
641 lpara[count++] = msptr->client_p->name;
642 *mbuf++ = 'v';
645 else if(is_voiced(msptr))
647 msptr->flags &= ~CHFL_VOICE;
648 lpara[count++] = msptr->client_p->name;
649 *mbuf++ = 'v';
651 else
652 continue;
654 if(count >= MAXMODEPARAMS)
656 *mbuf = '\0';
657 sendto_channel_local(ALL_MEMBERS, chptr,
658 ":%s MODE %s %s %s %s %s %s",
659 me.name, chptr->chname, lmodebuf,
660 lpara[0], lpara[1], lpara[2], lpara[3]);
661 mbuf = lmodebuf;
662 *mbuf++ = '-';
663 count = 0;
665 for(i = 0; i < MAXMODEPARAMS; i++)
666 lpara[i] = NULL;
670 if(count != 0)
672 *mbuf = '\0';
673 sendto_channel_local(ALL_MEMBERS, chptr,
674 ":%s MODE %s %s %s %s %s %s",
675 me.name, chptr->chname, lmodebuf,
676 EmptyString(lpara[0]) ? "" : lpara[0],
677 EmptyString(lpara[1]) ? "" : lpara[1],
678 EmptyString(lpara[2]) ? "" : lpara[2],
679 EmptyString(lpara[3]) ? "" : lpara[3]);
684 /* remove_ban_list()
686 * inputs - channel, source, list to remove, char of mode, caps needed
687 * outputs -
688 * side effects - given list is removed, with modes issued to local clients
689 * and non-TS6 servers.
691 static void
692 remove_ban_list(struct Channel *chptr, struct Client *source_p,
693 dlink_list *list, char c, int cap, int mems)
695 static char lmodebuf[BUFSIZE];
696 static char lparabuf[BUFSIZE];
697 struct Ban *banptr;
698 dlink_node *ptr;
699 dlink_node *next_ptr;
700 char *pbuf;
701 int count = 0;
702 int cur_len, mlen, plen;
704 pbuf = lparabuf;
706 cur_len = mlen = ircsprintf(lmodebuf, ":%s MODE %s -",
707 source_p->name, chptr->chname);
708 mbuf = lmodebuf + mlen;
710 DLINK_FOREACH_SAFE(ptr, next_ptr, list->head)
712 banptr = ptr->data;
714 /* trailing space, and the mode letter itself */
715 plen = strlen(banptr->banstr) + 2;
717 if(count >= MAXMODEPARAMS || (cur_len + plen) > BUFSIZE - 4)
719 /* remove trailing space */
720 *mbuf = '\0';
721 *(pbuf - 1) = '\0';
723 sendto_channel_local(mems, chptr, "%s %s",
724 lmodebuf, lparabuf);
725 sendto_server(source_p, chptr, cap, CAP_TS6,
726 "%s %s", lmodebuf, lparabuf);
728 cur_len = mlen;
729 mbuf = lmodebuf + mlen;
730 pbuf = lparabuf;
731 count = 0;
734 *mbuf++ = c;
735 cur_len += plen;
736 pbuf += ircsprintf(pbuf, "%s ", banptr->banstr);
737 count++;
739 free_ban(banptr);
742 *mbuf = '\0';
743 *(pbuf - 1) = '\0';
744 sendto_channel_local(mems, chptr, "%s %s", lmodebuf, lparabuf);
745 sendto_server(source_p, chptr, cap, CAP_TS6,
746 "%s %s", lmodebuf, lparabuf);
748 list->head = list->tail = NULL;
749 list->length = 0;