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