]> jfr.im git - irc/quakenet/newserv.git/blob - localuser/localuser.c
Add iterators to newsearch, along with an example channel iterator function.
[irc/quakenet/newserv.git] / localuser / localuser.c
1 /* localuser.c */
2
3 #include "../nick/nick.h"
4 #include "../lib/base64.h"
5 #include "../lib/sstring.h"
6 #include "../irc/irc.h"
7 #include "../irc/irc_config.h"
8 #include "../core/hooks.h"
9 #include "../core/error.h"
10 #include "../lib/version.h"
11 #include "localuser.h"
12
13 #include <string.h>
14 #include <stdarg.h>
15 #include <stdio.h>
16
17 MODULE_VERSION("");
18
19 int currentlocalunum;
20 UserMessageHandler umhandlers[MAXLOCALUSER+1];
21
22 typedef struct pendingkill {
23 nick *source, *target;
24 sstring *reason;
25 struct pendingkill *next;
26 } pendingkill;
27
28 pendingkill *pendingkilllist;
29
30 void checklocalkill(int hooknum, void *nick);
31 void clearpendingkills(int hooknum, void *arg);
32 void checkpendingkills(int hooknum, void *arg);
33 void _killuser(nick *source, nick *target, char *reason);
34
35 void _init() {
36 int i;
37
38 for (i=0;i<=MAXLOCALUSER;i++) {
39 umhandlers[i]=NULL;
40 }
41 currentlocalunum=1;
42 pendingkilllist=NULL;
43 registerhook(HOOK_IRC_SENDBURSTNICKS,&sendnickburst);
44 registerhook(HOOK_NICK_KILL,&checklocalkill);
45 registerhook(HOOK_NICK_LOSTNICK,&checkpendingkills); /* CHECK ME -> should this hook KILL or LOSTNICK or BOTH */
46 registerhook(HOOK_CORE_ENDOFHOOKSQUEUE,&clearpendingkills);
47 registerserverhandler("P",&handleprivatemsgcmd,2);
48 registerserverhandler("O",&handleprivatenoticecmd, 2);
49 }
50
51 void _fini() {
52 pendingkill *pk;
53
54 for (pk=pendingkilllist;pk;pk=pendingkilllist) {
55 pendingkilllist = pk->next;
56 freesstring(pk->reason);
57 free(pk);
58 }
59
60 deregisterhook(HOOK_IRC_SENDBURSTNICKS,&sendnickburst);
61 deregisterhook(HOOK_NICK_KILL,&checklocalkill);
62 deregisterhook(HOOK_NICK_LOSTNICK,&checkpendingkills); /* CHECK ME -> should this hook KILL or LOSTNICK or BOTH */
63 deregisterhook(HOOK_CORE_ENDOFHOOKSQUEUE,&clearpendingkills);
64
65 deregisterserverhandler("P",&handleprivatemsgcmd);
66 deregisterserverhandler("O",&handleprivatenoticecmd);
67 }
68
69 /*
70 * registerlocaluserwithuseridflags:
71 * This function creates a local user, and broadcasts it's existence to the net (if connected).
72 */
73
74 nick *registerlocaluserwithuseridflags(char *nickname, char *ident, char *host, char *realname, char *authname, unsigned long authid, flag_t umodes, flag_t accountflags, UserMessageHandler handler) {
75 int i;
76 nick *newuser,*np;
77 struct irc_in_addr ipaddress;
78
79 i=0;
80 currentlocalunum=(currentlocalunum+1)%262142;
81 while (servernicks[numerictolong(mynumeric->content,2)][currentlocalunum&MAXLOCALUSER]!=NULL) {
82 /* Numeric 262143 on our server is used for "nouser" by the channels module, so cannot be allocated */
83 currentlocalunum=(currentlocalunum+1)%262142;
84 if (++i>MAXLOCALUSER) {
85 return NULL;
86 }
87 }
88
89 /* This code is very similar to stuff in nick.c... */
90 newuser=newnick();
91 newuser->numeric=(numerictolong(mynumeric->content,2)<<18)|(currentlocalunum);
92 strncpy(newuser->nick,nickname,NICKLEN);
93 newuser->nick[NICKLEN]='\0';
94 strncpy(newuser->ident,ident,USERLEN);
95 newuser->ident[USERLEN]='\0';
96 newuser->host=findorcreatehost(host);
97 newuser->realname=findorcreaterealname(realname);
98 newuser->nextbyhost=newuser->host->nicks;
99 newuser->host->nicks=newuser;
100 newuser->nextbyrealname=newuser->realname->nicks;
101 newuser->realname->nicks=newuser;
102 newuser->umodes=umodes;
103
104 memset(&ipaddress, 0, sizeof(ipaddress));
105 ((unsigned short *)(ipaddress.in6_16))[5] = 65535;
106 ((unsigned short *)(ipaddress.in6_16))[6] = 127;
107 ((unsigned char *)(ipaddress.in6_16))[14] = 1;
108 ((unsigned char *)(ipaddress.in6_16))[15] = (currentlocalunum%253)+1;
109
110 newuser->ipnode = refnode(iptree, &ipaddress, PATRICIA_MAXBITS);
111 node_increment_usercount(newuser->ipnode);
112
113 newuser->timestamp=getnettime();
114 newuser->shident=NULL;
115 newuser->sethost=NULL;
116 newuser->marker=0;
117 memset(newuser->exts, 0, MAXNICKEXTS * sizeof(void *));
118
119 if (IsOper(newuser)) {
120 newuser->opername = getsstring("-", ACCOUNTLEN);
121 } else {
122 newuser->opername = NULL;
123 }
124 if (IsAccount(newuser)) {
125 strncpy(newuser->authname,authname,ACCOUNTLEN);
126 newuser->accountts=newuser->timestamp;
127 if (authid) {
128 newuser->auth=findorcreateauthname(authid, newuser->authname);
129 newuser->auth->usercount++;
130 newuser->nextbyauthname=newuser->auth->nicks;
131 newuser->auth->nicks=newuser;
132 newuser->auth->flags=accountflags;
133 } else {
134 newuser->auth=NULL;
135 }
136 } else {
137 newuser->authname[0]='\0';
138 newuser->accountts=0;
139 newuser->auth=NULL;
140 }
141
142 if (connected) {
143 /* Check for nick collision */
144 if ((np=getnickbynick(nickname))!=NULL) {
145 /* Make sure we will win the collision */
146 newuser->timestamp=(np->timestamp-1);
147 killuser(NULL, np, "Nick collision");
148 }
149 sendnickmsg(newuser);
150 }
151
152 if (handler!=NULL) {
153 umhandlers[(currentlocalunum&MAXLOCALUSER)]=handler;
154 }
155
156 *(gethandlebynumeric(newuser->numeric))=newuser;
157 addnicktohash(newuser);
158 triggerhook(HOOK_NICK_NEWNICK,newuser);
159
160 return newuser;
161 }
162
163 /*
164 * renamelocaluser:
165 * This function changes the name of a given local user
166 */
167
168 int renamelocaluser(nick *np, char *newnick) {
169 nick *np2;
170 char ipbuf[25];
171 time_t timestamp=getnettime();
172
173 if (!np)
174 return -1;
175
176 if (strlen(newnick) > NICKLEN)
177 return -1;
178
179 if (homeserver(np->numeric)!=mylongnum)
180 return -1;
181
182 if ((np2=getnickbynick(newnick))) {
183 if (np2==np) {
184 /* Case only name change */
185 strncpy(np->nick,newnick,NICKLEN);
186 np->nick[NICKLEN]='\0';
187 irc_send("%s N %s %d",iptobase64(ipbuf, &(np->p_ipaddr), sizeof(ipbuf), 1),np->nick,np->timestamp);
188 triggerhook(HOOK_NICK_RENAME,np);
189 return 0;
190 } else {
191 /* Kill other user and drop through */
192 timestamp=np2->timestamp-1;
193 killuser(NULL, np2, "Nick collision");
194 }
195 }
196
197 np->timestamp=timestamp;
198 removenickfromhash(np);
199 strncpy(np->nick,newnick,NICKLEN);
200 np->nick[NICKLEN]='\0';
201 addnicktohash(np);
202 irc_send("%s N %s %d",longtonumeric(np->numeric,5),np->nick,np->timestamp);
203 triggerhook(HOOK_NICK_RENAME,np);
204
205 return 0;
206 }
207
208 /*
209 * deregisterlocaluser:
210 * This function removes the given local user from the network
211 */
212
213 int deregisterlocaluser(nick *np, char *reason) {
214 long numeric;
215 char reasonstr[512];
216 void *harg[2];
217
218 if (np==NULL || (homeserver(np->numeric)!=mylongnum)) {
219 /* Non-existent user, or user not on this server */
220 return -1;
221 }
222
223 if (reason==NULL || *reason=='\0') {
224 sprintf(reasonstr,"Quit");
225 } else {
226 snprintf(reasonstr,510,"Quit: %s",reason);
227 }
228
229 harg[0]=np;
230 harg[1]=reasonstr;
231
232 triggerhook(HOOK_NICK_QUIT, harg);
233
234 numeric=np->numeric;
235 umhandlers[np->numeric&MAXLOCALUSER]=NULL;
236 deletenick(np);
237 if (connected) {
238 irc_send("%s Q :%s",longtonumeric(numeric,5),reasonstr);
239 }
240
241 return 0;
242 }
243
244 /*
245 * hooklocaluserhandler:
246 * This function adds a new handler to the hook chain for a local user
247 * THIS RELIES ON MODULES BEING UNLOADED IN THE CORRECT ORDER.
248 */
249 UserMessageHandler hooklocaluserhandler(nick *np, UserMessageHandler newhandler) {
250 UserMessageHandler oldhandler = NULL;
251 if (np==NULL || (homeserver(np->numeric)!=mylongnum)) {
252 /* Non-existent user, or user not on this server */
253 return NULL;
254 }
255 oldhandler = umhandlers[np->numeric&MAXLOCALUSER];
256 umhandlers[np->numeric&MAXLOCALUSER]=newhandler;
257 return oldhandler;
258 }
259
260 /*
261 * sendnickmsg:
262 * Sends details of a given local nick to the network.
263 */
264
265 void sendnickmsg(nick *np) {
266 char numericbuf[6];
267 char ipbuf[25], operbuf[ACCOUNTLEN + 5];
268 char accountbuf[100];
269
270 strncpy(numericbuf,longtonumeric(np->numeric,5),5);
271 numericbuf[5]='\0';
272
273 if(IsOper(np) && (serverlist[myhub].flags & SMODE_OPERNAME)) {
274 snprintf(operbuf,sizeof(operbuf)," %s",np->opername?np->opername->content:"-");
275 } else {
276 operbuf[0] = '\0';
277 }
278
279 if (IsAccount(np)) {
280 if (np->auth) {
281 if(np->auth->flags) {
282 snprintf(accountbuf,sizeof(accountbuf)," %s:%ld:%lu:" FLAG_T_SPECIFIER,np->authname,np->accountts,np->auth->userid,np->auth->flags);
283 } else {
284 snprintf(accountbuf,sizeof(accountbuf)," %s:%ld:%lu",np->authname,np->accountts,np->auth->userid);
285 }
286 } else if (np->accountts) {
287 snprintf(accountbuf,sizeof(accountbuf)," %s:%ld",np->authname,np->accountts);
288 } else {
289 snprintf(accountbuf,sizeof(accountbuf)," %s",np->authname);
290 }
291 } else {
292 accountbuf[0]='\0';
293 }
294
295 irc_send("%s N %s 1 %ld %s %s %s%s%s %s %s :%s",
296 mynumeric->content,np->nick,np->timestamp,np->ident,np->host->name->content,
297 printflags(np->umodes,umodeflags),operbuf,accountbuf,iptobase64(ipbuf,&(np->p_ipaddr),
298 sizeof(ipbuf),1),numericbuf,np->realname->name->content);
299 }
300
301 void sendnickburst(int hooknum, void *arg) {
302 /* Send nick messages for all local users */
303 nick **nh;
304 int i;
305
306 nh=servernicks[numerictolong(mynumeric->content,2)];
307 for (i=0;i<=MAXLOCALUSER;i++) {
308 if (nh[i]!=NULL) {
309 sendnickmsg(nh[i]);
310 }
311 }
312 }
313
314 /* Check for a kill of a local user */
315 void checklocalkill(int hooknum, void *arg) {
316 void **args=arg;
317 nick *target=args[0];
318 char *reason=args[1];
319 long numeric;
320
321 void *myargs[1];
322 myargs[0]=reason;
323
324
325 numeric=((nick *)target)->numeric;
326
327 if (homeserver(numeric)==mylongnum) {
328 if (umhandlers[(numeric)&MAXLOCALUSER]!=NULL) {
329 (umhandlers[(numeric)&MAXLOCALUSER])((nick *)target,LU_KILLED,myargs);
330 }
331 }
332 }
333
334 int handleprivatemsgcmd(void *source, int cargc, char **cargv) {
335 return handlemessageornotice(source, cargc, cargv, 0);
336 }
337
338 int handleprivatenoticecmd(void *source, int cargc, char **cargv) {
339 return handlemessageornotice(source, cargc, cargv, 1);
340 }
341
342 /* Handle privmsg or notice command addressed to user */
343 int handlemessageornotice(void *source, int cargc, char **cargv, int isnotice) {
344 nick *sender;
345 nick *target;
346 char *ch;
347 char targetnick[NICKLEN+1];
348 int foundat;
349 void *nargs[2];
350 int i;
351
352 /* Should have target and message */
353 if (cargc<2) {
354 return CMD_OK;
355 }
356
357 if (cargv[0][0]=='#' || cargv[0][0]=='+') {
358 /* Channel message/notice */
359 return CMD_OK;
360 }
361
362 if ((sender=getnickbynumericstr((char *)source))==NULL) {
363 Error("localuser",ERR_WARNING,"PRIVMSG from non existant user %s",(char *)source);
364 return CMD_OK;
365 }
366
367 /* Check for a "secure" message (foo@bar) */
368 foundat=0;
369 for (i=0,ch=cargv[0];(i<=NICKLEN) && (*ch);i++,ch++) {
370 if (*ch=='@') {
371 targetnick[i]='\0';
372 foundat=1;
373 break;
374 } else {
375 targetnick[i]=*ch;
376 }
377 }
378
379 if (!foundat) { /* Didn't find an @ sign, assume it's a numeric */
380 if ((target=getnickbynumericstr(cargv[0]))==NULL) {
381 Error("localuser",ERR_DEBUG,"Couldn't find target for %s",cargv[0]);
382 return CMD_OK;
383 }
384 } else { /* Did find @, do a lookup by nick */
385 if ((target=getnickbynick(targetnick))==NULL) {
386 Error("localuser",ERR_DEBUG,"Couldn't find target for %s",cargv[0]);
387 irc_send(":%s 401 %s %s :No such nick",myserver->content,sender->nick,cargv[0]);
388 return CMD_OK;
389 }
390 }
391
392 if (homeserver(target->numeric)!=mylongnum) {
393 Error("localuser",ERR_WARNING,"Got message/notice for someone not on my server");
394 irc_send(":%s 401 %s %s :No such nick",myserver->content,sender->nick,cargv[0]);
395 return CMD_OK;
396 }
397
398 if (foundat && !IsService(target)) {
399 Error("localuser",ERR_DEBUG,"Received secure message for %s, but user is not a service",cargv[0]);
400 irc_send(":%s 401 %s %s :No such nick",myserver->content,sender->nick,cargv[0]);
401 return CMD_OK;
402 }
403
404 if (umhandlers[(target->numeric)&MAXLOCALUSER]==NULL) {
405 /* No handler anyhow.. */
406 return CMD_OK;
407 }
408
409 /* Dispatch the message. */
410 nargs[0]=(void *)sender;
411 nargs[1]=(void *)cargv[1];
412 (umhandlers[(target->numeric)&MAXLOCALUSER])(target,isnotice?LU_PRIVNOTICE:(foundat?LU_SECUREMSG:LU_PRIVMSG),nargs);
413
414 return CMD_OK;
415 }
416
417 /* Send message to user */
418 void sendmessagetouser(nick *source, nick *target, char *format, ... ) {
419 char buf[BUFSIZE];
420 char senderstr[6];
421 va_list va;
422
423 longtonumeric2(source->numeric,5,senderstr);
424
425 va_start(va,format);
426 /* 10 bytes of numeric, 5 bytes of fixed format + terminator = 17 bytes */
427 /* So max sendable message is 495 bytes. Of course, a client won't be able
428 * to receive this.. */
429
430 vsnprintf(buf,BUFSIZE-17,format,va);
431 va_end(va);
432
433 if (homeserver(target->numeric)!=mylongnum)
434 irc_send("%s P %s :%s",senderstr,longtonumeric(target->numeric,5),buf);
435 }
436
437 /* Send messageto server, we don't check, but noones going to want to put a server pointer in anyway... */
438 void sendsecuremessagetouser(nick *source, nick *target, char *servername, char *format, ... ) {
439 char buf[BUFSIZE];
440 char senderstr[6];
441 va_list va;
442
443 longtonumeric2(source->numeric,5,senderstr);
444
445 va_start(va,format);
446 /* 10 bytes of numeric, 5 bytes of fixed format + terminator = 17 bytes */
447 /* So max sendable message is 495 bytes. Of course, a client won't be able
448 * to receive this.. */
449
450 vsnprintf(buf,BUFSIZE-17,format,va);
451 va_end(va);
452
453 if (homeserver(target->numeric)!=mylongnum)
454 irc_send("%s P %s@%s :%s",senderstr,target->nick,servername,buf);
455 }
456
457 /* Send notice to user */
458 void sendnoticetouser(nick *source, nick *target, char *format, ... ) {
459 char buf[BUFSIZE];
460 char senderstr[6];
461 va_list va;
462
463 longtonumeric2(source->numeric,5,senderstr);
464
465 va_start(va,format);
466 /* 10 bytes of numeric, 5 bytes of fixed format + terminator = 17 bytes */
467 /* So max sendable message is 495 bytes. Of course, a client won't be able
468 * to receive this.. */
469
470 vsnprintf(buf,BUFSIZE-17,format,va);
471 va_end(va);
472
473 if (homeserver(target->numeric)!=mylongnum)
474 irc_send("%s O %s :%s",senderstr,longtonumeric(target->numeric,5),buf);
475 }
476
477 /* Kill user */
478 void killuser(nick *source, nick *target, char *format, ... ) {
479 char buf[BUFSIZE];
480 va_list va;
481 pendingkill *pk;
482
483 va_start(va, format);
484 vsnprintf(buf, BUFSIZE-17, format, va);
485 va_end (va);
486
487 if (hookqueuelength) {
488 for (pk = pendingkilllist; pk; pk = pk->next)
489 if (pk->target == target)
490 return;
491
492 Error("localuser", ERR_DEBUG, "Adding pending kill for %s", target->nick);
493 pk = (pendingkill *)malloc(sizeof(pendingkill));
494 pk->source = source;
495 pk->target = target;
496 pk->reason = getsstring(buf, BUFSIZE);
497 pk->next = pendingkilllist;
498 pendingkilllist = pk;
499 } else {
500 _killuser(source, target, buf);
501 }
502 }
503
504 void sethostuser(nick *target, char *ident, char *host) {
505 irc_send("%s SH %s %s %s", mynumeric->content, longtonumeric(target->numeric, 5), ident, host);
506 }
507
508 void _killuser(nick *source, nick *target, char *reason) {
509 char senderstr[6];
510 char sourcestring[HOSTLEN+NICKLEN+3];
511
512 if (!source) {
513 /* If we have a null nick, use the server.. */
514 strcpy(senderstr, mynumeric->content);
515 strcpy(sourcestring, myserver->content);
516 } else {
517 strcpy(senderstr, longtonumeric(source->numeric,5));
518 sprintf(sourcestring,"%s!%s",source->host->name->content, source->nick);
519 }
520
521 irc_send("%s D %s :%s (%s)",senderstr,longtonumeric(target->numeric,5),sourcestring,reason);
522 deletenick(target);
523 }
524
525 void clearpendingkills(int hooknum, void *arg) {
526 pendingkill *pk;
527
528 pk = pendingkilllist;
529 while (pk) {
530 pendingkilllist = pk->next;
531
532 if (pk->target) {
533 Error("localuser", ERR_DEBUG, "Processing pending kill for %s", pk->target->nick);
534 _killuser(pk->source, pk->target, pk->reason->content);
535 }
536
537 freesstring(pk->reason);
538 free(pk);
539 pk = pendingkilllist;
540 }
541 }
542
543 void checkpendingkills(int hooknum, void *arg) {
544 nick *np = (nick *)arg;
545 pendingkill *pk;
546
547 for (pk=pendingkilllist; pk; pk = pk->next) {
548 if (pk->source == np) {
549 Error("localuser", ERR_INFO, "Pending kill source %s got deleted, NULL'ing source for pending kill", np->nick);
550 pk->source = NULL;
551 }
552 if (pk->target == np) {
553 Error("localuser", ERR_INFO, "Pending kill target %s got deleted, NULL'ing target for pending kill", np->nick);
554 pk->target = NULL;
555 }
556 }
557 }
558
559 /* Auth user */
560 void localusersetaccountwithuseridflagsts(nick *np, char *accname, unsigned long accid, flag_t accountflags, time_t authTS) {
561 if (IsAccount(np)) {
562 Error("localuser",ERR_WARNING,"Tried to set account on user %s already authed", np->nick);
563 return;
564 }
565
566 SetAccount(np);
567 strncpy(np->authname, accname, ACCOUNTLEN);
568 np->authname[ACCOUNTLEN]='\0';
569 np->accountts=authTS?authTS:getnettime();
570
571 if (accid) {
572 np->auth=findorcreateauthname(accid, accname);
573 np->auth->usercount++;
574 np->nextbyauthname=np->auth->nicks;
575 np->auth->nicks=np;
576 np->auth->flags=accountflags;
577 } else {
578 np->auth=NULL;
579 }
580
581 if (connected) {
582 if (np->auth) {
583 if (np->auth->flags) {
584 irc_send("%s AC %s %s %ld %lu %lu",mynumeric->content, longtonumeric(np->numeric,5), np->authname, np->accountts, np->auth->userid, np->auth->flags);
585 } else {
586 irc_send("%s AC %s %s %ld %lu",mynumeric->content, longtonumeric(np->numeric,5), np->authname, np->accountts, np->auth->userid);
587 }
588 } else {
589 irc_send("%s AC %s %s %ld",mynumeric->content, longtonumeric(np->numeric,5), np->authname, np->accountts);
590 }
591 }
592
593 triggerhook(HOOK_NICK_ACCOUNT, np);
594 }
595
596 void localusersetumodes(nick *np, flag_t newmodes) {
597 if (connected) {
598 irc_send("%s M %s %s", longtonumeric(np->numeric,5), np->nick, printflagdiff(np->umodes, newmodes, umodeflags));
599 }
600
601 np->umodes = newmodes;
602 }