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
24 * $Id: m_sjoin.c 22848 2006-07-05 13:35:49Z jilles $
32 #include "irc_string.h"
33 #include "sprintf_irc.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 $");
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
];
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
);
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
;
89 static struct Mode mode
, *oldmode
;
92 int keep_our_modes
= 1;
93 int keep_new_modes
= 1;
96 int mlen_nick
, mlen_uid
;
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]))
114 if(!IsChannelName(parv
[2]) || !check_channel_name(parv
[2]))
117 /* SJOIN's for local channels can't happen. */
121 modebuf
[0] = parabuf
[0] = mode
.key
[0] = '\0';
122 pargs
= mode
.mode
= mode
.limit
= 0;
125 newts
= atol(parv
[1]);
133 mode
.mode
|= MODE_INVITEONLY
;
136 mode
.mode
|= MODE_NOPRIVMSGS
;
139 mode
.mode
|= MODE_PRIVATE
;
142 mode
.mode
|= MODE_SECRET
;
145 mode
.mode
|= MODE_MODERATED
;
148 mode
.mode
|= MODE_TOPICLIMIT
;
150 #ifdef ENABLE_SERVICES
152 mode
.mode
|= MODE_REGONLY
;
156 strlcpy(mode
.key
, parv
[4 + args
], sizeof(mode
.key
));
162 mode
.limit
= atoi(parv
[4 + args
]);
172 /* remove any leading spaces */
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
);
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
)
206 chptr
->channelts
= newts
;
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
);
225 /* Lost the TS, other side wins, so remove modes on this side */
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
);
236 sendto_channel_local(ALL_MEMBERS
, chptr
, ":%s MODE %s %s %s",
237 source_p
->name
, chptr
->chname
,
240 *modebuf
= *parabuf
= '\0';
242 if(parv
[3][0] != '0' && keep_new_modes
)
243 modes
= channel_modes(chptr
, source_p
);
247 mlen_nick
= ircsprintf(buf_nick
, ":%s SJOIN %ld %s %s :",
248 source_p
->name
, (long) chptr
->channelts
,
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
,
258 ptr_uid
= buf_uid
+ mlen_uid
;
261 para
[0] = para
[1] = para
[2] = para
[3] = empty
;
263 len_nick
= len_uid
= 0;
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
271 if((p
= strchr(s
, ' ')) != NULL
)
280 for (i
= 0; i
< 2; i
++)
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
))
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
,
307 ptr_nick
= buf_nick
+ mlen_nick
;
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
,
316 ptr_uid
= buf_uid
+ mlen_uid
;
338 /* copy the nick to the two buffers */
339 len
= ircsprintf(ptr_nick
, "%s ", target_p
->name
);
342 len
= ircsprintf(ptr_uid
, "%s ", use_id(target_p
));
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",
359 target_p
->username
, target_p
->host
, parv
[2]);
366 para
[pargs
++] = target_p
->name
;
368 /* a +ov user.. bleh */
371 /* its possible the +o has filled up MAXMODEPARAMS, if so, start
374 if(pargs
>= MAXMODEPARAMS
)
377 sendto_channel_local(ALL_MEMBERS
, chptr
,
378 ":%s MODE %s %s %s %s %s %s",
379 source_p
->name
, chptr
->chname
,
381 para
[0], para
[1], para
[2], para
[3]);
384 para
[0] = para
[1] = para
[2] = para
[3] = NULL
;
389 para
[pargs
++] = target_p
->name
;
392 else if(fl
& CHFL_VOICE
)
395 para
[pargs
++] = target_p
->name
;
398 if(pargs
>= MAXMODEPARAMS
)
401 sendto_channel_local(ALL_MEMBERS
, chptr
,
402 ":%s MODE %s %s %s %s %s %s",
405 modebuf
, para
[0], para
[1], para
[2], para
[3]);
408 para
[0] = para
[1] = para
[2] = para
[3] = NULL
;
413 /* p points to the next nick */
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'))
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
))
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]));
445 destroy_channel(chptr
);
450 *(ptr_nick
- 1) = '\0';
451 *(ptr_uid
- 1) = '\0';
453 sendto_server(client_p
->from
, NULL
, CAP_TS6
, NOCAPS
,
455 sendto_server(client_p
->from
, NULL
, NOCAPS
, CAP_TS6
,
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
);
489 static struct mode_letter flags
[] = {
490 {MODE_NOPRIVMSGS
, 'n'},
491 {MODE_TOPICLIMIT
, 't'},
493 {MODE_MODERATED
, 'm'},
494 {MODE_INVITEONLY
, 'i'},
496 #ifdef ENABLE_SERVICES
503 set_final_mode(struct Mode
*mode
, struct Mode
*oldmode
)
505 int dir
= MODE_QUERY
;
506 char *pbuf
= parabuf
;
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
))
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
))
534 *mbuf
++ = flags
[i
].letter
;
538 if(oldmode
->limit
&& !mode
->limit
)
547 if(oldmode
->key
[0] && !mode
->key
[0])
555 len
= ircsprintf(pbuf
, "%s ", oldmode
->key
);
559 if(mode
->limit
&& oldmode
->limit
!= mode
->limit
)
567 len
= ircsprintf(pbuf
, "%d ", mode
->limit
);
571 if(mode
->key
[0] && strcmp(oldmode
->key
, mode
->key
))
579 len
= ircsprintf(pbuf
, "%s ", mode
->key
);
594 remove_our_modes(struct Channel
*chptr
, struct Client
*source_p
)
596 struct membership
*msptr
;
598 char lmodebuf
[MODEBUFLEN
];
599 const char *lpara
[MAXMODEPARAMS
];
606 for(i
= 0; i
< MAXMODEPARAMS
; i
++)
609 DLINK_FOREACH(ptr
, chptr
->members
.head
)
615 msptr
->flags
&= ~CHFL_CHANOP
;
616 lpara
[count
++] = msptr
->client_p
->name
;
619 /* +ov, might not fit so check. */
622 if(count
>= MAXMODEPARAMS
)
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],
631 /* preserve the initial '-' */
636 for(i
= 0; i
< MAXMODEPARAMS
; i
++)
640 msptr
->flags
&= ~CHFL_VOICE
;
641 lpara
[count
++] = msptr
->client_p
->name
;
645 else if(is_voiced(msptr
))
647 msptr
->flags
&= ~CHFL_VOICE
;
648 lpara
[count
++] = msptr
->client_p
->name
;
654 if(count
>= MAXMODEPARAMS
)
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]);
665 for(i
= 0; i
< MAXMODEPARAMS
; i
++)
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]);
686 * inputs - channel, source, list to remove, char of mode, caps needed
688 * side effects - given list is removed, with modes issued to local clients
689 * and non-TS6 servers.
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
];
699 dlink_node
*next_ptr
;
702 int cur_len
, mlen
, plen
;
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
)
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 */
723 sendto_channel_local(mems
, chptr
, "%s %s",
725 sendto_server(source_p
, chptr
, cap
, CAP_TS6
,
726 "%s %s", lmodebuf
, lparabuf
);
729 mbuf
= lmodebuf
+ mlen
;
736 pbuf
+= ircsprintf(pbuf
, "%s ", banptr
->banstr
);
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
;