]> jfr.im git - irc/quakenet/newserv.git/blob - localuser/localuserchannel.c
r645@blue (orig r493): slug | 2006-05-15 23:31:10 +0100
[irc/quakenet/newserv.git] / localuser / localuserchannel.c
1 /* Functions for manipulating local users on channels */
2
3 #include "localuser.h"
4 #include "localuserchannel.h"
5 #include "../nick/nick.h"
6 #include "../channel/channel.h"
7 #include "../irc/irc.h"
8
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <assert.h>
12
13 int handlechannelmsgcmd(void *source, int cargc, char **cargv);
14 int handlechannelnoticecmd(void *source, int cargc, char **cargv);
15 int handleinvitecmd(void *source, int cargc, char **cargv);
16 void luc_handlekick(int hooknum, void *arg);
17
18 void _init() {
19 registerserverhandler("P",&handlechannelmsgcmd,2);
20 registerserverhandler("O",&handlechannelnoticecmd,2);
21 registerserverhandler("I",&handleinvitecmd,2);
22 registerhook(HOOK_CHANNEL_KICK, luc_handlekick);
23 }
24
25 void _fini() {
26 deregisterserverhandler("P",&handlechannelmsgcmd);
27 deregisterserverhandler("O",&handlechannelnoticecmd);
28 deregisterserverhandler("I",&handleinvitecmd);
29 deregisterhook(HOOK_CHANNEL_KICK, luc_handlekick);
30 }
31
32 void luc_handlekick(int hooknum, void *arg) {
33 void **args=arg;
34 nick *target=args[1];
35 void *myargs[3];
36
37 if (homeserver(target->numeric)!=mylongnum)
38 return;
39
40 if (umhandlers[target->numeric & MAXLOCALUSER]) {
41 myargs[0]=args[2];
42 myargs[1]=args[0];
43 myargs[2]=args[3];
44
45 (umhandlers[target->numeric & MAXLOCALUSER])(target, LU_KICKED, myargs);
46 }
47 }
48
49 /* invites look something like:
50 * XXyyy I TargetNick :#channel
51 */
52
53 int handleinvitecmd(void *source, int cargc, char **cargv) {
54 void *nargs[2];
55 nick *sender;
56 channel *cp;
57 nick *target;
58
59 if (cargc<2) {
60 return CMD_OK;
61 }
62
63 if (!(sender=getnickbynumericstr(source))) {
64 Error("localuserchannel",ERR_WARNING,"Got invite from unknown numeric %s.",source);
65 return CMD_OK;
66 }
67
68 if (!(target=getnickbynick(cargv[0]))) {
69 Error("localuserchannel",ERR_WARNING,"Got invite for unknown local user %s.",cargv[0]);
70 return CMD_OK;
71 }
72
73 if (!(cp=findchannel(cargv[1]))) {
74 Error("localuserchannel",ERR_WARNING,"Got invite for non-existent channel %s.",cargv[1]);
75 return CMD_OK;
76 }
77
78 if (homeserver(target->numeric) != mylongnum) {
79 Error("localuserchannel",ERR_WARNING,"Got invite for non-local user %s.",target->nick);
80 return CMD_OK;
81 }
82
83 /* This is a valid race condition.. */
84 if (getnumerichandlefromchanhash(cp->users, target->numeric)) {
85 Error("localuserchannel",ERR_DEBUG,"Got invite for user %s already on %s.",target->nick, cp->index->name->content);
86 return CMD_OK;
87 }
88
89 nargs[0]=(void *)sender;
90 nargs[1]=(void *)cp;
91
92 if (umhandlers[target->numeric&MAXLOCALUSER]) {
93 (umhandlers[target->numeric&MAXLOCALUSER])(target, LU_INVITE, nargs);
94 }
95
96 return CMD_OK;
97 }
98
99 int handlechannelmsgcmd(void *source, int cargc, char **cargv) {
100 void *nargs[3];
101 nick *sender;
102 channel *target;
103 nick *np;
104 unsigned long numeric;
105 int i;
106 int found=0;
107
108 if (cargc<2) {
109 return CMD_OK;
110 }
111
112 if (cargv[0][0]!='#' && cargv[0][0]!='+') {
113 /* Not a channel message */
114 return CMD_OK;
115 }
116
117 if ((sender=getnickbynumericstr((char *)source))==NULL) {
118 Error("localuserchannel",ERR_DEBUG,"PRIVMSG from non existant user %s",(char *)source);
119 return CMD_OK;
120 }
121
122 if ((target=findchannel(cargv[0]))==NULL) {
123 Error("localuserchannel",ERR_DEBUG,"PRIVMSG to non existant channel %s",cargv[0]);
124 return CMD_OK;
125 }
126
127 /* OK, we have a valid channel the message was sent to. Let's look to see
128 * if we have any local users on there. Set up the arguments first as they are
129 * always going to be the same. */
130
131 nargs[0]=(void *)sender;
132 nargs[1]=(void *)target;
133 nargs[2]=(void *)cargv[1];
134
135 for (found=0,i=0;i<target->users->hashsize;i++) {
136 numeric=target->users->content[i];
137 if (numeric!=nouser && homeserver(numeric&CU_NUMERICMASK)==mylongnum) {
138 /* OK, it's one of our users.. we need to deal with it */
139 found++;
140 if (umhandlers[numeric&MAXLOCALUSER]) {
141 if ((np=getnickbynumeric(numeric))==NULL) {
142 Error("localuserchannel",ERR_ERROR,"PRIVMSG to channel user who doesn't exist (?) on %s",cargv[0]);
143 continue;
144 }
145 if (!IsDeaf(np)) {
146 (umhandlers[numeric&MAXLOCALUSER])(np,LU_CHANMSG,nargs);
147 } else {
148 found--;
149 }
150 }
151 }
152 }
153
154 if (!found) {
155 Error("localuserchannel",ERR_DEBUG,"Couldn't find any local targets for PRIVMSG to %s",cargv[0]);
156 }
157
158 return CMD_OK;
159 }
160
161 /*
162 * Added by Cruicky so S2 can receive channel notices
163 * Shameless rip of the above with s/privmsg/notice
164 */
165 int handlechannelnoticecmd(void *source, int cargc, char **cargv) {
166 void *nargs[3];
167 nick *sender;
168 channel *target;
169 nick *np;
170 unsigned long numeric;
171 int i;
172 int found=0;
173
174 if (cargc<2) {
175 return CMD_OK;
176 }
177
178 if (cargv[0][0]!='#' && cargv[0][0]!='+') {
179 /* Not a channel notice */
180 return CMD_OK;
181 }
182
183 if ((sender=getnickbynumericstr((char *)source))==NULL) {
184 Error("localuserchannel",ERR_DEBUG,"NOTICE from non existant user %s",(char *)source);
185 return CMD_OK;
186 }
187
188 if ((target=findchannel(cargv[0]))==NULL) {
189 Error("localuserchannel",ERR_DEBUG,"NOTICE to non existant channel %s",cargv[0]);
190 return CMD_OK;
191 }
192
193 /* OK, we have a valid channel the notice was sent to. Let's look to see
194 * if we have any local users on there. Set up the arguments first as they are
195 * always going to be the same. */
196
197 nargs[0]=(void *)sender;
198 nargs[1]=(void *)target;
199 nargs[2]=(void *)cargv[1];
200
201 for (found=0,i=0;i<target->users->hashsize;i++) {
202 numeric=target->users->content[i];
203 if (numeric!=nouser && homeserver(numeric&CU_NUMERICMASK)==mylongnum) {
204 /* OK, it's one of our users.. we need to deal with it */
205 found++;
206 if (umhandlers[numeric&MAXLOCALUSER]) {
207 if ((np=getnickbynumeric(numeric))==NULL) {
208 Error("localuserchannel",ERR_ERROR,"NOTICE to channel user who doesn't exist (?) on %s",cargv[0]);
209 continue;
210 }
211 if (!IsDeaf(np)) {
212 (umhandlers[numeric&MAXLOCALUSER])(np,LU_CHANNOTICE,nargs);
213 } else {
214 found--;
215 }
216 }
217 }
218 }
219
220 if (!found) {
221 Error("localuserchannel",ERR_DEBUG,"Couldn't find any local targets for NOTICE to %s",cargv[0]);
222 }
223
224 return CMD_OK;
225 }
226
227 int localjoinchannel(nick *np, channel *cp) {
228 void *harg[2];
229
230 if (cp==NULL || np==NULL) {
231 return 1;
232 }
233
234 /* Check that the user _is_ a local one.. */
235 if (homeserver(np->numeric)!=mylongnum) {
236 return 1;
237 }
238
239 /* Check that the user isn't on the channel already */
240 if ((getnumerichandlefromchanhash(cp->users,np->numeric))!=NULL) {
241 return 1;
242 }
243
244 /* OK, join the channel.. */
245 addnicktochannel(cp,np->numeric);
246
247 /* Trigger the event */
248 harg[0]=cp;
249 harg[1]=np;
250
251 triggerhook(HOOK_CHANNEL_JOIN, harg);
252
253 if (connected) {
254 irc_send("%s J %s %lu",longtonumeric(np->numeric,5),cp->index->name->content,cp->timestamp);
255 }
256 return 0;
257 }
258
259 int localpartchannel(nick *np, channel *cp) {
260 void *harg[3];
261
262 /* Check pointers are valid.. */
263 if (cp==NULL || np==NULL) {
264 Error("localuserchannel",ERR_WARNING,"Trying to part NULL channel or NULL nick (cp=%x,np=%x)",cp,np);
265 return 1;
266 }
267
268 /* And that user is local.. */
269 if (homeserver(np->numeric)!=mylongnum) {
270 Error("localuserchannel",ERR_WARNING,"Trying to part remote user %s",np->nick);
271 return 1;
272 }
273
274 /* Check that user is on channel */
275 if (getnumerichandlefromchanhash(cp->users,np->numeric)==NULL) {
276 Error("localuserchannel",ERR_WARNING,"Trying to part user %s from channel %s it is not on",np->nick,cp->index->name->content);
277 return 1;
278 }
279
280 if (connected) {
281 irc_send("%s L %s",longtonumeric(np->numeric,5),cp->index->name->content);
282 }
283
284 harg[0]=cp;
285 harg[1]=np;
286 harg[2]=NULL;
287 triggerhook(HOOK_CHANNEL_PART,harg);
288
289 /* Now leave the channel */
290 delnickfromchannel(cp,np->numeric,1);
291
292 return 0;
293 }
294
295 int localcreatechannel(nick *np, char *channame) {
296 channel *cp;
297
298 /* Check that the user _is_ a local one.. */
299 if (homeserver(np->numeric)!=mylongnum) {
300 return 1;
301 }
302
303 if ((cp=findchannel(channame))!=NULL) {
304 /* Already exists */
305 return 1;
306 }
307
308 cp=createchannel(channame);
309 cp->timestamp=getnettime();
310
311 /* Add the local user to the channel, preopped */
312 addnicktochannel(cp,(np->numeric)|CUMODE_OP);
313
314 if (connected) {
315 irc_send("%s C %s %ld",longtonumeric(np->numeric,5),cp->index->name->content,cp->timestamp);
316 }
317
318 triggerhook(HOOK_CHANNEL_NEWCHANNEL,(void *)cp);
319 return 0;
320 }
321
322 int localgetops(nick *np, channel *cp) {
323 unsigned long *lp;
324
325 /* Check that the user _is_ a local one.. */
326 if (homeserver(np->numeric)!=mylongnum) {
327 return 1;
328 }
329
330 if ((lp=getnumerichandlefromchanhash(cp->users,np->numeric))==NULL) {
331 return 1;
332 }
333
334 if (*lp & CUMODE_OP) {
335 /* already opped */
336 return 1;
337 }
338
339 /* Op the user */
340 (*lp)|=CUMODE_OP;
341
342 if (connected) {
343 irc_send("%s M %s +o %s",mynumeric->content,cp->index->name->content,longtonumeric(np->numeric,5));
344 }
345
346 return 0;
347 }
348
349 int localgetvoice(nick *np, channel *cp) {
350 unsigned long *lp;
351
352 /* Check that the user _is_ a local one.. */
353 if (homeserver(np->numeric)!=mylongnum) {
354 return 1;
355 }
356
357 if ((lp=getnumerichandlefromchanhash(cp->users,np->numeric))==NULL) {
358 return 1;
359 }
360
361 if (*lp & CUMODE_VOICE) {
362 /* already opped */
363 return 1;
364 }
365
366 /* Voice the user */
367 (*lp)|=CUMODE_VOICE;
368
369 if (connected) {
370 irc_send("%s M %s +v %s",mynumeric->content,cp->index->name->content,longtonumeric(np->numeric,5));
371 }
372
373 return 0;
374 }
375
376 /*
377 * localsetmodeinit:
378 * Initialises the modechanges structure.
379 */
380
381 void localsetmodeinit (modechanges *changes, channel *cp, nick *np) {
382 changes->cp=cp;
383 changes->source=np;
384 changes->changecount=0;
385 changes->addflags=0;
386 changes->delflags=0;
387 }
388
389 /*
390 * localdosetmode_ban:
391 * Set or clear a ban on the channel
392 */
393
394 void localdosetmode_ban (modechanges *changes, const char *ban, short dir) {
395 sstring *bansstr=getsstring(ban,HOSTLEN+NICKLEN+USERLEN+5);
396
397 /* If we're told to clear a ban that isn't here, do nothing. */
398 if (dir==MCB_DEL && !clearban(changes->cp, bansstr->content, 1))
399 return;
400
401 if (changes->changecount >= MAXMODEARGS)
402 localsetmodeflush(changes, 0);
403
404 changes->changes[changes->changecount].str=bansstr;
405 changes->changes[changes->changecount].flag='b';
406 changes->changes[changes->changecount++].dir=dir;
407
408 if (dir==MCB_ADD) {
409 setban(changes->cp, bansstr->content);
410 }
411 }
412
413 /*
414 * localdosetmode_key:
415 * Set or clear a key on the channel
416 */
417
418 void localdosetmode_key (modechanges *changes, const char *key, short dir) {
419 int i,j;
420 sstring *keysstr;
421
422 /* Check we have space in the buffer */
423 if (changes->changecount >= MAXMODEARGS)
424 localsetmodeflush(changes,0);
425
426 if (dir==MCB_ADD) {
427 /* Get a copy of the key for use later */
428 keysstr=getsstring(key, KEYLEN);
429
430 /* Check there isn't a key set/clear in the pipeline already.. */
431 for (i=0;i<changes->changecount;i++) {
432 if (changes->changes[i].flag=='k') {
433 /* There's a change already.. */
434 if (changes->changes[i].dir==MCB_ADD) {
435 /* Already an add key change. Here, we just replace the key
436 * we were going to add with this one. Note that we need to
437 * free the current cp->key, and the changes.str */
438 freesstring(changes->changes[i].str);
439 changes->changes[i].str=keysstr;
440 freesstring(changes->cp->key);
441 changes->cp->key=getsstring(key, KEYLEN);
442 /* That's it, we're done */
443 return;
444 } else {
445 /* There was a command to delete key.. we need to flush
446 * this out then add the new key (-k+k isn't valid).
447 * Make sure this gets flushed, then drop through to common code */
448 localsetmodeflush(changes, 1);
449 }
450 }
451 }
452
453 /* We got here, so there's no change pending already .. check for key on chan */
454 if (IsKey(changes->cp)) {
455 if (sstringcompare(changes->cp->key, keysstr)) {
456 /* Key is set and different. Need to put -k in and flush changes now */
457 changes->changes[changes->changecount].str=changes->cp->key; /* implicit free */
458 changes->changes[changes->changecount].dir=MCB_DEL;
459 changes->changes[changes->changecount++].flag='k';
460 localsetmodeflush(changes, 1); /* Note that this will free the sstring on the channel */
461 } else {
462 /* Key is set and the same: do nothing. */
463 freesstring(keysstr); /* Don't need this after all */
464 return;
465 }
466 }
467
468 /* If we get here, there's no key on the channel right now and nothing in the buffer to
469 * add or remove one */
470 SetKey(changes->cp);
471 changes->cp->key=getsstring(key, KEYLEN);
472
473 changes->changes[changes->changecount].str=keysstr;
474 changes->changes[changes->changecount].dir=MCB_ADD;
475 changes->changes[changes->changecount++].flag='k';
476 } else {
477 /* We're removing a key.. */
478 /* Only bother if key is set atm */
479 if (IsKey(changes->cp)) {
480 ClearKey(changes->cp);
481 for(i=0;i<changes->changecount;i++) {
482 if (changes->changes[i].flag=='k') {
483 /* We were already doing something with a key..
484 * it MUST be adding one */
485 assert(changes->changes[i].dir==MCB_ADD);
486 /* Just forget the earlier change */
487 freesstring(changes->changes[i].str);
488 changes->changecount--;
489 for (j=i;j<changes->changecount;j++) {
490 changes->changes[j]=changes->changes[j+1];
491 }
492 /* Explicitly free key on chan */
493 freesstring(changes->cp->key);
494 changes->cp->key=NULL;
495 return;
496 }
497 }
498
499 /* We didn't hit a key change, so put a remove command in */
500 changes->changes[changes->changecount].str=changes->cp->key; /* implicit free */
501 changes->cp->key=NULL;
502 changes->changes[changes->changecount].dir=MCB_DEL;
503 changes->changes[changes->changecount++].flag='k';
504 }
505 }
506 }
507
508 void localdosetmode_limit (modechanges *changes, unsigned int limit, short dir) {
509 int i;
510 char buf[20];
511
512 if (dir==MCB_DEL) {
513 localdosetmode_simple(changes, 0, CHANMODE_LIMIT);
514 return;
515 }
516
517 /* Kill redundant changes */
518 if ((IsLimit(changes->cp) && changes->cp->limit==limit) || !limit)
519 return;
520
521 SetLimit(changes->cp);
522 changes->cp->limit=limit;
523 changes->delflags &= ~CHANMODE_LIMIT;
524
525 /* Check for existing limit add */
526 for (i=0;i<changes->changecount;i++) {
527 if (changes->changes[i].flag=='l') {
528 /* must be add */
529 freesstring(changes->changes[i].str);
530 sprintf(buf,"%u",limit);
531 changes->changes[i].str=getsstring(buf,20);
532 return;
533 }
534 }
535
536 /* None, add new one. Note that we can do +l even if a limit is already set */
537 if (changes->changecount >= MAXMODEARGS)
538 localsetmodeflush(changes,0);
539
540 changes->changes[changes->changecount].flag='l';
541 sprintf(buf,"%u",limit);
542 changes->changes[changes->changecount].str=getsstring(buf,20);
543 changes->changes[changes->changecount++].dir=MCB_ADD;
544 }
545
546 void localdosetmode_simple (modechanges *changes, flag_t addmodes, flag_t delmodes) {
547 int i,j;
548
549 /* We can't add a mode we're deleting, a key, limit, or mode that's already set */
550 addmodes &= ~(delmodes | CHANMODE_KEY | CHANMODE_LIMIT | changes->cp->flags);
551
552 /* We can't delete a key or a mode that's not set */
553 delmodes &= (~(CHANMODE_KEY) & changes->cp->flags);
554
555 /* If we're doing +p, do -s as well, and vice versa */
556 /* Also disallow +ps */
557 if (addmodes & CHANMODE_SECRET) {
558 addmodes &= ~(CHANMODE_PRIVATE);
559 delmodes |= (CHANMODE_PRIVATE & changes->cp->flags);
560 }
561
562 if (addmodes & CHANMODE_PRIVATE) {
563 delmodes |= (CHANMODE_SECRET & changes->cp->flags);
564 }
565
566 /* Fold changes into channel */
567 changes->cp->flags |= addmodes;
568 changes->cp->flags &= ~delmodes;
569
570 if (delmodes & CHANMODE_LIMIT) {
571 /* Check for +l in the parametered changes */
572 for (i=0;i<changes->changecount;i++) {
573 if (changes->changes[i].flag=='l') {
574 freesstring(changes->changes[i].str);
575 changes->changecount--;
576 for (j=0;j<changes->changecount;j++) {
577 changes->changes[j]=changes->changes[j+1];
578 }
579 }
580 break;
581 }
582 changes->cp->limit=0;
583 }
584
585 /* And into the changes buffer */
586 changes->addflags &= ~delmodes;
587 changes->addflags |= addmodes;
588
589 changes->delflags &= ~addmodes;
590 changes->delflags |= delmodes;
591 }
592
593 /*
594 * localdosetmode:
595 * Applies a mode change.
596 */
597
598 void localdosetmode_nick (modechanges *changes, nick *target, short modes) {
599 unsigned long *lp;
600
601 if ((lp=getnumerichandlefromchanhash(changes->cp->users,target->numeric))==NULL) {
602 /* Target isn't on channel, abort */
603 return;
604 }
605
606 if ((modes & MC_DEOP) && (*lp & CUMODE_OP)) {
607 (*lp) &= ~CUMODE_OP;
608 if (changes->changecount >= MAXMODEARGS)
609 localsetmodeflush(changes, 0);
610 changes->changes[changes->changecount].str=getsstring(longtonumeric(target->numeric,5),5);
611 changes->changes[changes->changecount].dir=MCB_DEL;
612 changes->changes[changes->changecount++].flag='o';
613 }
614
615 if ((modes & MC_DEVOICE) && (*lp & CUMODE_VOICE)) {
616 (*lp) &= ~CUMODE_VOICE;
617 if (changes->changecount >= MAXMODEARGS)
618 localsetmodeflush(changes, 0);
619 changes->changes[changes->changecount].str=getsstring(longtonumeric(target->numeric,5),5);
620 changes->changes[changes->changecount].dir=MCB_DEL;
621 changes->changes[changes->changecount++].flag='v';
622 }
623
624 if ((modes & MC_OP) && !(modes & MC_DEOP) && !(*lp & CUMODE_OP)) {
625 (*lp) |= CUMODE_OP;
626 if (changes->changecount >= MAXMODEARGS)
627 localsetmodeflush(changes, 0);
628 changes->changes[changes->changecount].str=getsstring(longtonumeric(target->numeric,5),5);
629 changes->changes[changes->changecount].dir=MCB_ADD;
630 changes->changes[changes->changecount++].flag='o';
631 }
632
633 if ((modes & MC_VOICE) && !(modes & MC_DEVOICE) && !(*lp & CUMODE_VOICE)) {
634 (*lp) |= CUMODE_VOICE;
635 if (changes->changecount >= MAXMODEARGS)
636 localsetmodeflush(changes, 0);
637 changes->changes[changes->changecount].str=getsstring(longtonumeric(target->numeric,5),5);
638 changes->changes[changes->changecount].dir=MCB_ADD;
639 changes->changes[changes->changecount++].flag='v';
640 }
641
642 }
643
644 /*
645 * localsetmodeflush:
646 * Sends out mode changes to the network.
647 */
648
649 void localsetmodeflush (modechanges *changes, int flushall) {
650 int i,j=0;
651 unsigned long *lp;
652 char source[6];
653
654 char addmodes[26];
655 int ampos=0;
656 char remmodes[26];
657 int rmpos=0;
658
659 char addargs[500];
660 int aapos=0;
661 char remargs[500];
662 int rapos=0;
663
664 strcpy(addmodes, printflags_noprefix(changes->addflags, cmodeflags));
665 ampos=strlen(addmodes);
666
667 strcpy(remmodes, printflags_noprefix(changes->delflags, cmodeflags));
668 rmpos=strlen(remmodes);
669
670 changes->addflags=changes->delflags=0;
671
672 for (i=0;i<changes->changecount;i++) {
673 /* Don't overflow the string, kinda nasty to work out.. format is: */
674 /* AAAA M #chan +add-rem (addstr) (remstr) */
675 if ((changes->cp->index->name->length + aapos + rapos +
676 ampos + rmpos + changes->changes[i].str->length + 20) > BUFSIZE)
677 break;
678
679 switch (changes->changes[i].dir) {
680 case MCB_ADD:
681 addmodes[ampos++]=changes->changes[i].flag;
682 aapos+=sprintf(addargs+aapos, "%s ", changes->changes[i].str->content);
683 break;
684
685 case MCB_DEL:
686 remmodes[rmpos++]=changes->changes[i].flag;
687 rapos+=sprintf(remargs+rapos, "%s ", changes->changes[i].str->content);
688 break;
689 }
690 freesstring(changes->changes[i].str);
691 }
692
693 if (i<changes->changecount) {
694 for (j=i;j<changes->changecount;j++)
695 changes->changes[j-i]=changes->changes[j];
696 }
697
698 changes->changecount -= i;
699
700 if ((ampos+rmpos)==0) {
701 /* No changes */
702 return;
703 }
704
705 addmodes[ampos]='\0';
706 remmodes[rmpos]='\0';
707 addargs[aapos]='\0';
708 remargs[rapos]='\0';
709
710 if (changes->source==NULL ||
711 (lp=getnumerichandlefromchanhash(changes->cp->users,changes->source->numeric))==NULL) {
712 /* User isn't on channel, hack mode */
713 strcpy(source,mynumeric->content);
714 } else {
715 /* Check the user is local */
716 if (homeserver(changes->source->numeric)!=mylongnum) {
717 return;
718 }
719 if ((*lp&CUMODE_OP)==0) {
720 localgetops(changes->source,changes->cp);
721 }
722 strcpy(source,longtonumeric(changes->source->numeric,5));
723 }
724
725 if (connected) {
726 irc_send("%s M %s %s%s%s%s %s%s",source,changes->cp->index->name->content,
727 rmpos ? "-" : "", remmodes,
728 ampos ? "+" : "", addmodes, remargs, addargs);
729 }
730
731 /* If we have to flush everything out but didn't finish, go round again */
732 if (changes->changecount && flushall)
733 localsetmodeflush(changes, 1);
734 }
735
736 /*
737 * localsetmodes:
738 * Sets modes for the user on the channel. This is now a stub routine that
739 * uses the new functions.
740 */
741
742 int localsetmodes(nick *np, channel *cp, nick *target, short modes) {
743 modechanges changes;
744
745 localsetmodeinit (&changes, cp, np);
746 localdosetmode_nick (&changes, target, modes);
747 localsetmodeflush (&changes, 1);
748
749 return 0;
750 }
751
752 /* This function just sends the actual mode change,
753 * it assumes that the actual channel modes have been changed appropriately.
754 * This is unfortunately inconsistent with the rest of the localuser API..
755 */
756
757 void localusermodechange(nick *np, channel *cp, char *modes) {
758 char source[10];
759 unsigned long *lp;
760
761 if (np==NULL || (lp=getnumerichandlefromchanhash(cp->users,np->numeric))==NULL) {
762 /* User isn't on channel, hack mode */
763 strcpy(source,mynumeric->content);
764 } else {
765 /* Check the user is local */
766 if (homeserver(np->numeric)!=mylongnum) {
767 return;
768 }
769 if ((*lp&CUMODE_OP)==0) {
770 localgetops(np,cp);
771 }
772 strcpy(source,longtonumeric(np->numeric,5));
773 }
774
775 if (connected) {
776 irc_send("%s M %s %s",source,cp->index->name->content,modes);
777 }
778 }
779
780 /* This function actually sets the topic itself though.. a bit inconsistent :/ */
781
782 void localsettopic(nick *np, channel *cp, char *topic) {
783 unsigned long *lp;
784 char source[10];
785
786 if (np==NULL || (lp=getnumerichandlefromchanhash(cp->users,np->numeric))==NULL) {
787 /* User isn't on channel, hack mode */
788 strcpy(source,mynumeric->content);
789 } else {
790 /* Check the user is local */
791 if (homeserver(np->numeric)!=mylongnum) {
792 return;
793 }
794 if ((*lp&CUMODE_OP)==0 && IsTopicLimit(cp)) {
795 localgetops(np,cp);
796 }
797 strcpy(source,longtonumeric(np->numeric,5));
798 }
799
800 if (cp->topic) {
801 freesstring(cp->topic);
802 }
803
804 cp->topic=getsstring(topic,TOPICLEN);
805 cp->topictime=getnettime();
806
807 if (connected) {
808 irc_send("%s T %s %u %u :%s",source,cp->index->name->content,cp->timestamp,cp->topictime,(cp->topic)?cp->topic->content:"");
809 }
810 }
811
812 void localkickuser(nick *np, channel *cp, nick *target, const char *message) {
813 unsigned long *lp;
814 char source[10];
815
816 if (np==NULL || (lp=getnumerichandlefromchanhash(cp->users,np->numeric))==NULL) {
817 /* User isn't on channel, hack mode */
818 strcpy(source,mynumeric->content);
819 } else {
820 /* Check the user is local */
821 if (homeserver(np->numeric)!=mylongnum) {
822 return;
823 }
824 if ((*lp&CUMODE_OP)==0 && IsTopicLimit(cp)) {
825 localgetops(np,cp);
826 }
827 strcpy(source,longtonumeric(np->numeric,5));
828 }
829
830 if ((lp=getnumerichandlefromchanhash(cp->users,target->numeric))==NULL)
831 return;
832
833 /* Send the message to the network first in case delnickfromchannel()
834 * destroys the channel.. */
835 if (connected) {
836 irc_send("%s K %s %s :%s",source,cp->index->name->content,
837 longtonumeric(target->numeric,5), message);
838 }
839
840 delnickfromchannel(cp, target->numeric, 1);
841 }
842
843 void sendmessagetochannel(nick *source, channel *cp, char *format, ... ) {
844 char buf[BUFSIZE];
845 char senderstr[6];
846 va_list va;
847
848 if (!source)
849 return;
850
851 longtonumeric2(source->numeric,5,senderstr);
852
853 va_start(va,format);
854 /* 10 bytes of numeric, 5 bytes of fixed format + terminator = 17 bytes */
855 /* So max sendable message is 495 bytes. Of course, a client won't be able
856 * to receive this.. */
857
858 vsnprintf(buf,BUFSIZE-17,format,va);
859 va_end(va);
860
861 if (connected) {
862 irc_send("%s P %s :%s",senderstr,cp->index->name->content,buf);
863 }
864 }
865
866 void localinvite(nick *source, channel *cp, nick *target) {
867
868 /* Servers can't send invites */
869 if (!source)
870 return;
871
872 /* CHECK: Does the sender have to be on the relevant channel? */
873
874 /* For some reason invites are sent with the target nick as
875 * argument */
876 if (connected) {
877 irc_send("%s I %s :%s",longtonumeric(source->numeric,5),
878 target->nick, cp->index->name->content);
879 }
880 }
881