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