1 /* Functions for manipulating local users on channels */
4 #include "localuserchannel.h"
5 #include "../nick/nick.h"
6 #include "../channel/channel.h"
7 #include "../irc/irc.h"
8 #include "../lib/version.h"
9 #include "../lib/sstring.h"
19 typedef struct pendingkick
{
20 nick
*source
, *target
;
23 struct pendingkick
*next
;
26 pendingkick
*pendingkicklist
;
28 int handlechannelmsgcmd(void *source
, int cargc
, char **cargv
);
29 int handlechannelnoticecmd(void *source
, int cargc
, char **cargv
);
30 int handleinvitecmd(void *source
, int cargc
, char **cargv
);
31 void clearpendingkicks(int hooknum
, void *arg
);
32 void checkpendingkicknicks(int hooknum
, void *arg
);
33 void checkpendingkickchannels(int hooknum
, void *arg
);
34 void _localkickuser(nick
*np
, channel
*cp
, nick
*target
, const char *message
);
35 void luc_handlekick(int hooknum
, void *arg
);
39 registerserverhandler("P",&handlechannelmsgcmd
,2);
40 registerserverhandler("O",&handlechannelnoticecmd
,2);
41 registerserverhandler("I",&handleinvitecmd
,2);
42 registerhook(HOOK_CHANNEL_KICK
, luc_handlekick
);
43 registerhook(HOOK_NICK_LOSTNICK
, checkpendingkicknicks
);
44 registerhook(HOOK_CHANNEL_LOSTCHANNEL
, checkpendingkickchannels
);
45 registerhook(HOOK_CORE_ENDOFHOOKSQUEUE
,&clearpendingkicks
);
51 deregisterserverhandler("P",&handlechannelmsgcmd
);
52 deregisterserverhandler("O",&handlechannelnoticecmd
);
53 deregisterserverhandler("I",&handleinvitecmd
);
54 deregisterhook(HOOK_CHANNEL_KICK
, luc_handlekick
);
55 deregisterhook(HOOK_NICK_LOSTNICK
, checkpendingkicknicks
);
56 deregisterhook(HOOK_CHANNEL_LOSTCHANNEL
, checkpendingkickchannels
);
57 deregisterhook(HOOK_CORE_ENDOFHOOKSQUEUE
,&clearpendingkicks
);
59 for (pk
=pendingkicklist
;pk
;pk
=pendingkicklist
) {
60 pendingkicklist
= pk
->next
;
61 freesstring(pk
->reason
);
66 void luc_handlekick(int hooknum
, void *arg
) {
71 if (homeserver(target
->numeric
)!=mylongnum
)
74 if (umhandlers
[target
->numeric
& MAXLOCALUSER
]) {
79 (umhandlers
[target
->numeric
& MAXLOCALUSER
])(target
, LU_KICKED
, myargs
);
83 /* invites look something like:
84 * XXyyy I TargetNick :#channel
87 int handleinvitecmd(void *source
, int cargc
, char **cargv
) {
97 if (!(sender
=getnickbynumericstr(source
))) {
98 Error("localuserchannel",ERR_WARNING
,"Got invite from unknown numeric %s.",(char *)source
);
102 if (!(target
=getnickbynick(cargv
[0]))) {
103 Error("localuserchannel",ERR_WARNING
,"Got invite for unknown local user %s.",cargv
[0]);
107 if (!(cp
=findchannel(cargv
[1]))) {
108 Error("localuserchannel",ERR_WARNING
,"Got invite for non-existent channel %s.",cargv
[1]);
112 if (homeserver(target
->numeric
) != mylongnum
) {
113 Error("localuserchannel",ERR_WARNING
,"Got invite for non-local user %s.",target
->nick
);
117 /* This is a valid race condition.. */
118 if (getnumerichandlefromchanhash(cp
->users
, target
->numeric
)) {
119 Error("localuserchannel",ERR_DEBUG
,"Got invite for user %s already on %s.",target
->nick
, cp
->index
->name
->content
);
123 nargs
[0]=(void *)sender
;
126 if (umhandlers
[target
->numeric
&MAXLOCALUSER
]) {
127 (umhandlers
[target
->numeric
&MAXLOCALUSER
])(target
, LU_INVITE
, nargs
);
133 /* PRIVMSG/NOTICE to channel handling is identical up to the point where the hook is called. */
134 static int handlechannelmsgornotice(void *source
, int cargc
, char **cargv
, int isnotice
) {
139 unsigned long numeric
;
147 if (cargv
[0][0]!='#' && cargv
[0][0]!='+') {
148 /* Not a channel message */
152 if ((sender
=getnickbynumericstr((char *)source
))==NULL
) {
153 Error("localuserchannel",ERR_DEBUG
,"PRIVMSG/NOTICE from non existant user %s",(char *)source
);
157 if ((target
=findchannel(cargv
[0]))==NULL
) {
158 Error("localuserchannel",ERR_DEBUG
,"PRIVMSG/NOTICE to non existant channel %s",cargv
[0]);
162 /* OK, we have a valid channel the message was sent to. Let's look to see
163 * if we have any local users on there. Set up the arguments first as they are
164 * always going to be the same. */
166 nargs
[0]=(void *)sender
;
167 nargs
[1]=(void *)target
;
168 nargs
[2]=(void *)cargv
[1];
170 for (found
=0,i
=0;i
<target
->users
->hashsize
;i
++) {
171 numeric
=target
->users
->content
[i
];
172 if (numeric
!=nouser
&& homeserver(numeric
&CU_NUMERICMASK
)==mylongnum
) {
173 /* OK, it's one of our users.. we need to deal with it */
174 if (found
&& ((getnickbynumericstr((char *)source
)==NULL
) || ((findchannel(cargv
[0]))==NULL
) || !(getnumerichandlefromchanhash(target
->users
, sender
->numeric
)))) {
175 Error("localuserchannel", ERR_INFO
, "Nick or channel lost, or user no longer on channel in LU_CHANMSG");
179 if (umhandlers
[numeric
&MAXLOCALUSER
]) {
180 if ((np
=getnickbynumeric(numeric
))==NULL
) {
181 Error("localuserchannel",ERR_ERROR
,"PRIVMSG/NOTICE to channel user who doesn't exist (?) on %s",cargv
[0]);
185 (umhandlers
[numeric
&MAXLOCALUSER
])(np
,(isnotice
?LU_CHANNOTICE
:LU_CHANMSG
),nargs
);
194 Error("localuserchannel",ERR_DEBUG
,"Couldn't find any local targets for PRIVMSG/NOTICE to %s",cargv
[0]);
200 /* Wrapper functions to call the above code */
201 int handlechannelmsgcmd(void *source
, int cargc
, char **cargv
) {
202 return handlechannelmsgornotice(source
, cargc
, cargv
, 0);
205 int handlechannelnoticecmd(void *source
, int cargc
, char **cargv
) {
206 return handlechannelmsgornotice(source
, cargc
, cargv
, 1);
209 /* Burst onto channel. This replaces the timestamp and modes
210 * with the provided ones. Keys and limits use the provided ones
211 * if needed. nick * is optional, but joins the channel opped if
214 * Due to the way ircu works, this only works if the provided timestamp is
215 * older than the one currently on the channel. If the timestamps are
216 * equal, the modes are ignored, but the user (if any) is still allowed to
217 * join with op. If the provided timestamp is newer than the exsting one we
218 * just do a join instead - if you try to replace an old timestamp with a
219 * newer one ircu will just laugh at you (and you will be desynced).
221 int localburstontochannel(channel
*cp
, nick
*np
, time_t timestamp
, flag_t modes
, unsigned int limit
, char *key
) {
223 char extramodebuf
[512];
229 if (timestamp
> cp
->timestamp
) {
230 return localjoinchannel(np
, cp
);
233 if (timestamp
< cp
->timestamp
) {
234 cp
->timestamp
=timestamp
;
237 /* deal with key - if we need one use the provided one if set, otherwise
238 * the existing one, but if there is no existing one clear +k */
240 /* Sanitise the provided key - this might invalidate it.. */
245 /* Free old key, if any */
247 freesstring(cp
->key
);
249 cp
->key
=getsstring(key
,KEYLEN
);
255 /* Not +k - free the existing key, if any */
256 freesstring(cp
->key
);
272 /* We also need to blow away all other op/voice and bans on the
273 * channel. This is the same code we use when someone else does
276 for (i
=0;i
<cp
->users
->hashsize
;i
++) {
277 if (cp
->users
->content
[i
]!=nouser
) {
278 cp
->users
->content
[i
]&=CU_NUMERICMASK
;
283 /* Actually add the nick to the channel. Make sure it's a local nick and actually exists first. */
284 if (np
&& (homeserver(np
->numeric
) == mylongnum
) &&
285 !(getnumerichandlefromchanhash(cp
->users
,np
->numeric
))) {
286 addnicktochannel(cp
,(np
->numeric
)|CUMODE_OP
);
288 np
=NULL
; /* If we're not adding it here, don't send it later in the burst msg either */
292 /* actual burst message */
294 sprintf(nickbuf
," %s:o", longtonumeric(np
->numeric
,5));
300 sprintf(extramodebuf
," %d",cp
->limit
);
302 extramodebuf
[0]='\0';
305 /* XX B #channel <timestamp> +modes <limit> <key> <user> */
306 irc_send("%s B %s %lu %s%s%s%s%s",
307 mynumeric
->content
,cp
->index
->name
->content
,cp
->timestamp
,
308 printflags(cp
->flags
,cmodeflags
),extramodebuf
,
309 IsKey(cp
)?" ":"",IsKey(cp
)?cp
->key
->content
:"", nickbuf
);
312 /* Tell the world something happened... */
313 triggerhook(HOOK_CHANNEL_BURST
,cp
);
318 int localjoinchannel(nick
*np
, channel
*cp
) {
321 if (cp
==NULL
|| np
==NULL
) {
325 /* Check that the user _is_ a local one.. */
326 if (homeserver(np
->numeric
)!=mylongnum
) {
330 /* Check that the user isn't on the channel already */
331 if ((getnumerichandlefromchanhash(cp
->users
,np
->numeric
))!=NULL
) {
335 /* OK, join the channel.. */
336 addnicktochannel(cp
,np
->numeric
);
338 /* Trigger the event */
343 irc_send("%s J %s %lu",longtonumeric(np
->numeric
,5),cp
->index
->name
->content
,cp
->timestamp
);
346 triggerhook(HOOK_CHANNEL_JOIN
, harg
);
351 int localpartchannel(nick
*np
, channel
*cp
, char *reason
) {
354 /* Check pointers are valid.. */
355 if (cp
==NULL
|| np
==NULL
) {
356 Error("localuserchannel",ERR_WARNING
,"Trying to part NULL channel or NULL nick (cp=%p,np=%p)",cp
,np
);
360 /* And that user is local.. */
361 if (homeserver(np
->numeric
)!=mylongnum
) {
362 Error("localuserchannel",ERR_WARNING
,"Trying to part remote user %s",np
->nick
);
366 /* Check that user is on channel */
367 if (getnumerichandlefromchanhash(cp
->users
,np
->numeric
)==NULL
) {
368 Error("localuserchannel",ERR_WARNING
,"Trying to part user %s from channel %s it is not on",np
->nick
,cp
->index
->name
->content
);
374 irc_send("%s L %s :%s",longtonumeric(np
->numeric
,5),cp
->index
->name
->content
, reason
);
376 irc_send("%s L %s",longtonumeric(np
->numeric
,5),cp
->index
->name
->content
);
382 triggerhook(HOOK_CHANNEL_PART
,harg
);
384 /* Now leave the channel */
385 delnickfromchannel(cp
,np
->numeric
,1);
390 int localcreatechannel(nick
*np
, char *channame
) {
393 /* Check that the user _is_ a local one.. */
394 if (homeserver(np
->numeric
)!=mylongnum
) {
398 if ((cp
=findchannel(channame
))!=NULL
) {
403 cp
=createchannel(channame
);
404 cp
->timestamp
=getnettime();
406 /* Add the local user to the channel, preopped */
407 addnicktochannel(cp
,(np
->numeric
)|CUMODE_OP
);
410 irc_send("%s C %s %ld",longtonumeric(np
->numeric
,5),cp
->index
->name
->content
,cp
->timestamp
);
413 triggerhook(HOOK_CHANNEL_NEWCHANNEL
,(void *)cp
);
417 int localgetops(nick
*np
, channel
*cp
) {
420 /* Check that the user _is_ a local one.. */
421 if (homeserver(np
->numeric
)!=mylongnum
) {
425 if ((lp
=getnumerichandlefromchanhash(cp
->users
,np
->numeric
))==NULL
) {
429 if (*lp
& CUMODE_OP
) {
438 irc_send("%s M %s +o %s",mynumeric
->content
,cp
->index
->name
->content
,longtonumeric(np
->numeric
,5));
444 int localgetvoice(nick
*np
, channel
*cp
) {
447 /* Check that the user _is_ a local one.. */
448 if (homeserver(np
->numeric
)!=mylongnum
) {
452 if ((lp
=getnumerichandlefromchanhash(cp
->users
,np
->numeric
))==NULL
) {
456 if (*lp
& CUMODE_VOICE
) {
465 irc_send("%s M %s +v %s",mynumeric
->content
,cp
->index
->name
->content
,longtonumeric(np
->numeric
,5));
473 * Initialises the modechanges structure.
476 void localsetmodeinit (modechanges
*changes
, channel
*cp
, nick
*np
) {
479 changes
->changecount
=0;
485 * localdosetmode_ban:
486 * Set or clear a ban on the channel
489 void localdosetmode_ban (modechanges
*changes
, const char *ban
, short dir
) {
490 sstring
*bansstr
=getsstring(ban
,HOSTLEN
+NICKLEN
+USERLEN
+5);
492 /* If we're told to clear a ban that isn't here, do nothing. */
493 if (dir
==MCB_DEL
&& !clearban(changes
->cp
, bansstr
->content
, 1))
496 /* Similarly if someone is trying to add a completely overlapped ban, do
498 if (dir
==MCB_ADD
&& !setban(changes
->cp
, bansstr
->content
))
501 if (changes
->changecount
>= MAXMODEARGS
)
502 localsetmodeflush(changes
, 0);
504 changes
->changes
[changes
->changecount
].str
=bansstr
;
505 changes
->changes
[changes
->changecount
].flag
='b';
506 changes
->changes
[changes
->changecount
++].dir
=dir
;
509 setban(changes
->cp
, bansstr
->content
);
514 * localdosetmode_key:
515 * Set or clear a key on the channel
518 void localdosetmode_key (modechanges
*changes
, char *key
, short dir
) {
522 /* Check we have space in the buffer */
523 if (changes
->changecount
>= MAXMODEARGS
)
524 localsetmodeflush(changes
,0);
527 /* Sanitise the key. If this nullifies it then we give up */
532 /* Get a copy of the key for use later */
533 keysstr
=getsstring(key
, KEYLEN
);
535 /* Check there isn't a key set/clear in the pipeline already.. */
536 for (i
=0;i
<changes
->changecount
;i
++) {
537 if (changes
->changes
[i
].flag
=='k') {
538 /* There's a change already.. */
539 if (changes
->changes
[i
].dir
==MCB_ADD
) {
540 /* Already an add key change. Here, we just replace the key
541 * we were going to add with this one. Note that we need to
542 * free the current cp->key, and the changes.str */
543 freesstring(changes
->changes
[i
].str
);
544 changes
->changes
[i
].str
=keysstr
;
545 freesstring(changes
->cp
->key
);
546 changes
->cp
->key
=getsstring(key
, KEYLEN
);
547 /* That's it, we're done */
548 freesstring(keysstr
);
551 /* There was a command to delete key.. we need to flush
552 * this out then add the new key (-k+k isn't valid).
553 * Make sure this gets flushed, then drop through to common code */
554 localsetmodeflush(changes
, 1);
559 /* We got here, so there's no change pending already .. check for key on chan */
560 if (IsKey(changes
->cp
)) {
561 if (sstringcompare(changes
->cp
->key
, keysstr
)) {
562 /* Key is set and different. Need to put -k in and flush changes now */
563 changes
->changes
[changes
->changecount
].str
=changes
->cp
->key
; /* implicit free */
564 changes
->changes
[changes
->changecount
].dir
=MCB_DEL
;
565 changes
->changes
[changes
->changecount
++].flag
='k';
566 localsetmodeflush(changes
, 1); /* Note that this will free the sstring on the channel */
568 /* Key is set and the same: do nothing. */
569 freesstring(keysstr
); /* Don't need this after all */
574 /* If we get here, there's no key on the channel right now and nothing in the buffer to
575 * add or remove one */
577 changes
->cp
->key
=getsstring(key
, KEYLEN
);
579 changes
->changes
[changes
->changecount
].str
=keysstr
;
580 changes
->changes
[changes
->changecount
].dir
=MCB_ADD
;
581 changes
->changes
[changes
->changecount
++].flag
='k';
583 /* We're removing a key.. */
584 /* Only bother if key is set atm */
585 if (IsKey(changes
->cp
)) {
586 ClearKey(changes
->cp
);
587 for(i
=0;i
<changes
->changecount
;i
++) {
588 if (changes
->changes
[i
].flag
=='k') {
589 /* We were already doing something with a key..
590 * it MUST be adding one */
591 assert(changes
->changes
[i
].dir
==MCB_ADD
);
592 /* Just forget the earlier change */
593 freesstring(changes
->changes
[i
].str
);
594 changes
->changecount
--;
595 for (j
=i
;j
<changes
->changecount
;j
++) {
596 changes
->changes
[j
]=changes
->changes
[j
+1];
598 /* Explicitly free key on chan */
599 freesstring(changes
->cp
->key
);
600 changes
->cp
->key
=NULL
;
605 /* We didn't hit a key change, so put a remove command in */
606 changes
->changes
[changes
->changecount
].str
=changes
->cp
->key
; /* implicit free */
607 changes
->cp
->key
=NULL
;
608 changes
->changes
[changes
->changecount
].dir
=MCB_DEL
;
609 changes
->changes
[changes
->changecount
++].flag
='k';
614 void localdosetmode_limit (modechanges
*changes
, unsigned int limit
, short dir
) {
619 localdosetmode_simple(changes
, 0, CHANMODE_LIMIT
);
623 /* Kill redundant changes */
624 if ((IsLimit(changes
->cp
) && changes
->cp
->limit
==limit
) || !limit
)
627 SetLimit(changes
->cp
);
628 changes
->cp
->limit
=limit
;
629 changes
->delflags
&= ~CHANMODE_LIMIT
;
631 /* Check for existing limit add */
632 for (i
=0;i
<changes
->changecount
;i
++) {
633 if (changes
->changes
[i
].flag
=='l') {
635 freesstring(changes
->changes
[i
].str
);
636 sprintf(buf
,"%u",limit
);
637 changes
->changes
[i
].str
=getsstring(buf
,20);
642 /* None, add new one. Note that we can do +l even if a limit is already set */
643 if (changes
->changecount
>= MAXMODEARGS
)
644 localsetmodeflush(changes
,0);
646 changes
->changes
[changes
->changecount
].flag
='l';
647 sprintf(buf
,"%u",limit
);
648 changes
->changes
[changes
->changecount
].str
=getsstring(buf
,20);
649 changes
->changes
[changes
->changecount
++].dir
=MCB_ADD
;
652 void localdosetmode_simple (modechanges
*changes
, flag_t addmodes
, flag_t delmodes
) {
655 /* We can't add a mode we're deleting, a key, limit, or mode that's already set */
656 addmodes
&= ~(delmodes
| CHANMODE_KEY
| CHANMODE_LIMIT
| changes
->cp
->flags
);
658 /* We can't delete a key or a mode that's not set */
659 delmodes
&= (~(CHANMODE_KEY
) & changes
->cp
->flags
);
661 /* If we're doing +p, do -s as well, and vice versa */
662 /* Also disallow +ps */
663 if (addmodes
& CHANMODE_SECRET
) {
664 addmodes
&= ~(CHANMODE_PRIVATE
);
665 delmodes
|= (CHANMODE_PRIVATE
& changes
->cp
->flags
);
668 if (addmodes
& CHANMODE_PRIVATE
) {
669 delmodes
|= (CHANMODE_SECRET
& changes
->cp
->flags
);
672 /* Fold changes into channel */
673 changes
->cp
->flags
|= addmodes
;
674 changes
->cp
->flags
&= ~delmodes
;
676 if (delmodes
& CHANMODE_LIMIT
) {
677 /* Check for +l in the parametered changes */
678 for (i
=0;i
<changes
->changecount
;i
++) {
679 if (changes
->changes
[i
].flag
=='l') {
680 freesstring(changes
->changes
[i
].str
);
681 changes
->changecount
--;
682 for (j
=0;j
<changes
->changecount
;j
++) {
683 changes
->changes
[j
]=changes
->changes
[j
+1];
688 changes
->cp
->limit
=0;
691 /* And into the changes buffer */
692 changes
->addflags
&= ~delmodes
;
693 changes
->addflags
|= addmodes
;
695 changes
->delflags
&= ~addmodes
;
696 changes
->delflags
|= delmodes
;
701 * Applies a mode change.
704 void localdosetmode_nick (modechanges
*changes
, nick
*target
, short modes
) {
707 if ((lp
=getnumerichandlefromchanhash(changes
->cp
->users
,target
->numeric
))==NULL
) {
708 /* Target isn't on channel, abort */
712 if ((modes
& MC_DEOP
) && (*lp
& CUMODE_OP
)) {
714 if (changes
->changecount
>= MAXMODEARGS
)
715 localsetmodeflush(changes
, 0);
716 changes
->changes
[changes
->changecount
].str
=getsstring(longtonumeric(target
->numeric
,5),5);
717 changes
->changes
[changes
->changecount
].dir
=MCB_DEL
;
718 changes
->changes
[changes
->changecount
++].flag
='o';
721 if ((modes
& MC_DEVOICE
) && (*lp
& CUMODE_VOICE
)) {
722 (*lp
) &= ~CUMODE_VOICE
;
723 if (changes
->changecount
>= MAXMODEARGS
)
724 localsetmodeflush(changes
, 0);
725 changes
->changes
[changes
->changecount
].str
=getsstring(longtonumeric(target
->numeric
,5),5);
726 changes
->changes
[changes
->changecount
].dir
=MCB_DEL
;
727 changes
->changes
[changes
->changecount
++].flag
='v';
730 if ((modes
& MC_OP
) && !(modes
& MC_DEOP
) && !(*lp
& CUMODE_OP
)) {
732 if (changes
->changecount
>= MAXMODEARGS
)
733 localsetmodeflush(changes
, 0);
734 changes
->changes
[changes
->changecount
].str
=getsstring(longtonumeric(target
->numeric
,5),5);
735 changes
->changes
[changes
->changecount
].dir
=MCB_ADD
;
736 changes
->changes
[changes
->changecount
++].flag
='o';
739 if ((modes
& MC_VOICE
) && !(modes
& MC_DEVOICE
) && !(*lp
& CUMODE_VOICE
)) {
740 (*lp
) |= CUMODE_VOICE
;
741 if (changes
->changecount
>= MAXMODEARGS
)
742 localsetmodeflush(changes
, 0);
743 changes
->changes
[changes
->changecount
].str
=getsstring(longtonumeric(target
->numeric
,5),5);
744 changes
->changes
[changes
->changecount
].dir
=MCB_ADD
;
745 changes
->changes
[changes
->changecount
++].flag
='v';
752 * Sends out mode changes to the network.
755 void localsetmodeflush (modechanges
*changes
, int flushall
) {
770 strcpy(addmodes
, printflags_noprefix(changes
->addflags
, cmodeflags
));
771 ampos
=strlen(addmodes
);
773 strcpy(remmodes
, printflags_noprefix(changes
->delflags
, cmodeflags
));
774 rmpos
=strlen(remmodes
);
776 changes
->addflags
=changes
->delflags
=0;
778 for (i
=0;i
<changes
->changecount
;i
++) {
779 /* Don't overflow the string, kinda nasty to work out.. format is: */
780 /* AAAA M #chan +add-rem (addstr) (remstr) */
781 if ((changes
->cp
->index
->name
->length
+ aapos
+ rapos
+
782 ampos
+ rmpos
+ changes
->changes
[i
].str
->length
+ 20) > BUFSIZE
)
785 switch (changes
->changes
[i
].dir
) {
787 addmodes
[ampos
++]=changes
->changes
[i
].flag
;
788 aapos
+=sprintf(addargs
+aapos
, "%s ", changes
->changes
[i
].str
->content
);
792 remmodes
[rmpos
++]=changes
->changes
[i
].flag
;
793 rapos
+=sprintf(remargs
+rapos
, "%s ", changes
->changes
[i
].str
->content
);
796 freesstring(changes
->changes
[i
].str
);
799 if (i
<changes
->changecount
) {
800 for (j
=i
;j
<changes
->changecount
;j
++)
801 changes
->changes
[j
-i
]=changes
->changes
[j
];
804 changes
->changecount
-= i
;
806 if ((ampos
+rmpos
)==0) {
811 addmodes
[ampos
]='\0';
812 remmodes
[rmpos
]='\0';
816 if (changes
->source
==NULL
||
817 (lp
=getnumerichandlefromchanhash(changes
->cp
->users
,changes
->source
->numeric
))==NULL
) {
818 /* User isn't on channel, hack mode */
819 strcpy(source
,mynumeric
->content
);
821 /* Check the user is local */
822 if (homeserver(changes
->source
->numeric
)!=mylongnum
) {
825 if ((*lp
&CUMODE_OP
)==0) {
826 localgetops(changes
->source
,changes
->cp
);
828 strcpy(source
,longtonumeric(changes
->source
->numeric
,5));
832 irc_send("%s M %s %s%s%s%s %s%s",source
,changes
->cp
->index
->name
->content
,
833 rmpos
? "-" : "", remmodes
,
834 ampos
? "+" : "", addmodes
, remargs
, addargs
);
837 /* If we have to flush everything out but didn't finish, go round again */
838 if (changes
->changecount
&& flushall
)
839 localsetmodeflush(changes
, 1);
844 * Sets modes for the user on the channel. This is now a stub routine that
845 * uses the new functions.
848 int localsetmodes(nick
*np
, channel
*cp
, nick
*target
, short modes
) {
851 localsetmodeinit (&changes
, cp
, np
);
852 localdosetmode_nick (&changes
, target
, modes
);
853 localsetmodeflush (&changes
, 1);
858 /* This function just sends the actual mode change,
859 * it assumes that the actual channel modes have been changed appropriately.
860 * This is unfortunately inconsistent with the rest of the localuser API..
863 void localusermodechange(nick
*np
, channel
*cp
, char *modes
) {
867 if (np
==NULL
|| (lp
=getnumerichandlefromchanhash(cp
->users
,np
->numeric
))==NULL
) {
868 /* User isn't on channel, hack mode */
869 strcpy(source
,mynumeric
->content
);
871 /* Check the user is local */
872 if (homeserver(np
->numeric
)!=mylongnum
) {
875 if ((*lp
&CUMODE_OP
)==0) {
878 strcpy(source
,longtonumeric(np
->numeric
,5));
882 irc_send("%s M %s %s",source
,cp
->index
->name
->content
,modes
);
886 /* This function actually sets the topic itself though.. a bit inconsistent :/ */
888 void localsettopic(nick
*np
, channel
*cp
, char *topic
) {
891 time_t now
=getnettime();
893 if (np
==NULL
|| (lp
=getnumerichandlefromchanhash(cp
->users
,np
->numeric
))==NULL
) {
894 /* User isn't on channel, hack mode */
895 strcpy(source
,mynumeric
->content
);
897 /* Check the user is local */
898 if (homeserver(np
->numeric
)!=mylongnum
) {
901 if ((*lp
&CUMODE_OP
)==0 && IsTopicLimit(cp
)) {
904 strcpy(source
,longtonumeric(np
->numeric
,5));
908 freesstring(cp
->topic
);
911 cp
->topic
=getsstring(topic
,TOPICLEN
);
913 /* Update the topic time iff the old time was in the past. This
914 * means if we are bouncing a topic with a TS 1sec newer than ours
915 * we won't use an old timestamp */
916 if (cp
->topictime
< now
) {
921 irc_send("%s T %s %jd %jd :%s",source
,cp
->index
->name
->content
,(intmax_t)cp
->timestamp
,(intmax_t)cp
->topictime
,(cp
->topic
)?cp
->topic
->content
:"");
925 void localkickuser(nick
*np
, channel
*cp
, nick
*target
, const char *message
) {
928 if (hookqueuelength
) {
929 for (pk
= pendingkicklist
; pk
; pk
= pk
->next
)
930 if (pk
->target
== target
&& pk
->chan
== cp
)
933 Error("localuserchannel", ERR_DEBUG
, "Adding pending kick for %s on %s", target
->nick
, cp
->index
->name
->content
);
934 pk
= (pendingkick
*)malloc(sizeof(pendingkick
));
938 pk
->reason
= getsstring(message
, BUFSIZE
);
939 pk
->next
= pendingkicklist
;
940 pendingkicklist
= pk
;
942 _localkickuser(np
, cp
, target
, message
);
946 void _localkickuser(nick
*np
, channel
*cp
, nick
*target
, const char *message
) {
950 if (np
==NULL
|| (lp
=getnumerichandlefromchanhash(cp
->users
,np
->numeric
))==NULL
) {
951 /* User isn't on channel, hack mode */
952 strcpy(source
,mynumeric
->content
);
954 /* Check the user is local */
955 if (homeserver(np
->numeric
)!=mylongnum
) {
958 if ((*lp
&CUMODE_OP
)==0) {
961 strcpy(source
,longtonumeric(np
->numeric
,5));
964 if ((lp
=getnumerichandlefromchanhash(cp
->users
,target
->numeric
))==NULL
)
967 /* Send the message to the network first in case delnickfromchannel()
968 * destroys the channel.. */
970 irc_send("%s K %s %s :%s",source
,cp
->index
->name
->content
,
971 longtonumeric(target
->numeric
,5), message
);
974 delnickfromchannel(cp
, target
->numeric
, 1);
977 void clearpendingkicks(int hooknum
, void *arg
) {
980 pk
= pendingkicklist
;
982 pendingkicklist
= pk
->next
;
984 if (pk
->target
&& pk
->chan
) {
985 Error("localuserchannel", ERR_DEBUG
, "Processing pending kick for %s on %s", pk
->target
->nick
, pk
->chan
->index
->name
->content
);
986 _localkickuser(pk
->source
, pk
->chan
, pk
->target
, pk
->reason
->content
);
989 freesstring(pk
->reason
);
991 pk
= pendingkicklist
;
995 void checkpendingkicknicks(int hooknum
, void *arg
) {
996 nick
*np
= (nick
*)arg
;
999 for (pk
=pendingkicklist
; pk
; pk
= pk
->next
) {
1000 if (pk
->source
== np
) {
1001 Error("localuserchannel", ERR_INFO
, "Pending kick source %s got deleted, NULL'ing source for pending kick", np
->nick
);
1004 if (pk
->target
== np
) {
1005 Error("localuserchannel", ERR_INFO
, "Pending kick target %s got deleted, NULL'ing target for pending kick", np
->nick
);
1011 void checkpendingkickchannels(int hooknum
, void *arg
) {
1012 channel
*cp
= (channel
*)arg
;
1015 for (pk
=pendingkicklist
; pk
; pk
= pk
->next
) {
1016 if (pk
->chan
== cp
) {
1017 Error("localuserchannel", ERR_INFO
, "Pending kick channel %s got deleted, NULL'ing channel for pending kick", cp
->index
->name
->content
);
1023 void sendmessagetochannel(nick
*source
, channel
*cp
, char *format
, ... ) {
1031 longtonumeric2(source
->numeric
,5,senderstr
);
1033 va_start(va
,format
);
1034 /* 10 bytes of numeric, 5 bytes of fixed format + terminator = 17 bytes */
1035 /* So max sendable message is 495 bytes. Of course, a client won't be able
1036 * to receive this.. */
1038 vsnprintf(buf
,BUFSIZE
-17,format
,va
);
1042 irc_send("%s P %s :%s",senderstr
,cp
->index
->name
->content
,buf
);
1046 void sendopnoticetochannel(nick
*source
, channel
*cp
, char *format
, ... ) {
1054 longtonumeric2(source
->numeric
,5,senderstr
);
1056 va_start(va
,format
);
1057 /* 10 bytes of numeric, 5 bytes of fixed format + terminator = 17 bytes */
1058 /* So max sendable message is 495 bytes. Of course, a client won't be able
1059 * to receive this.. */
1061 vsnprintf(buf
,BUFSIZE
-17,format
,va
);
1065 irc_send("%s WC %s :%s",senderstr
,cp
->index
->name
->content
,buf
);
1069 void localinvite(nick
*source
, chanindex
*cip
, nick
*target
) {
1071 /* Servers can't send invites */
1075 /* CHECK: Does the sender have to be on the relevant channel? */
1077 /* For some reason invites are sent with the target nick as
1080 irc_send("%s I %s :%s",longtonumeric(source
->numeric
,5),
1081 target
->nick
, cip
->name
->content
);