]> jfr.im git - irc/quakenet/newserv.git/blame - nick/nickhandlers.c
CHANSERV: run ->lastactive updater once every hour.
[irc/quakenet/newserv.git] / nick / nickhandlers.c
CommitLineData
c86edd1d
Q
1/* nickhandlers.c */
2
3#include "nick.h"
4#include "../lib/flags.h"
5#include "../lib/irc_string.h"
6#include "../lib/base64.h"
7#include "../irc/irc.h"
8#include "../irc/irc_config.h"
9#include "../core/error.h"
10#include "../core/hooks.h"
11#include "../lib/sstring.h"
12#include "../server/server.h"
13#include "../parser/parser.h"
14#include <stdlib.h>
15#include <string.h>
8bab42e7 16#include <stdint.h>
c86edd1d
Q
17
18/*
19 * handlenickmsg:
20 * Handle new nicks being introduced to the network.
21 * Handle renames.
22 */
23
24int handlenickmsg(void *source, int cargc, char **cargv) {
25 char *sender=(char *)source;
26 time_t timestamp;
27 nick *np,*np2;
28 nick **nh;
29 char *fakehost;
30 char *accountts;
c153c0dc 31 char *accountflags;
526e7c1d 32 struct irc_in_addr ipaddress;
47657339
C
33 char *accountid;
34 unsigned long userid;
c86edd1d
Q
35
36 if (cargc==2) { /* rename */
2fdef282
CP
37 char oldnick[NICKLEN+1];
38 void *harg[2];
39
c86edd1d
Q
40 /* Nyklon 1017697578 */
41 timestamp=strtol(cargv[1],NULL,10);
42 np=getnickbynumericstr(sender);
43 if (np==NULL) {
44 Error("nick",ERR_ERROR,"Rename from non-existent sender %s",sender);
45 return CMD_OK;
46 }
2fdef282
CP
47
48 strncpy(oldnick,np->nick,NICKLEN);
49 oldnick[NICKLEN]='\0';
50 harg[0]=(void *)np;
51 harg[1]=(void *)oldnick;
52
c86edd1d
Q
53 np2=getnickbynick(cargv[0]);
54 if (np==np2) {
55 /* The new and old nickname have the same hash, this means a rename to the same name in
56 * different case, e.g. Flash -> flash. In this case the timestamp for the change should
57 * match the existing timestamp, and we can bypass all the collision checking and hash fettling. */
58 if (np->timestamp!=timestamp) {
16739dbe 59 Error("nick",ERR_WARNING,"Rename to same nickname with different timestamp (%s(%jd) -> %s(%jd))",
58a4da4a 60 np->nick,(intmax_t)np->timestamp,cargv[0], (intmax_t)timestamp);
c86edd1d
Q
61 np->timestamp=timestamp;
62 }
63 strncpy(np->nick,cargv[0],NICKLEN);
64 np->nick[NICKLEN]='\0';
2fdef282 65 triggerhook(HOOK_NICK_RENAME,harg);
c86edd1d
Q
66 return CMD_OK;
67 }
68 if (np2!=NULL) {
69 /* Nick collision */
70 if (ircd_strcmp(np->ident,np2->ident) || (np->host!=np2->host)) {
71 /* Different user@host */
72 if (np2->timestamp < timestamp) {
73 /* The nick attempting to rename got killed. Guess we don't need to do the rename now :) */
74 deletenick(np);
75 return CMD_OK;
76 } else {
77 /* The other nick got killed */
78 deletenick(np2);
79 }
80 } else {
81 if (np2->timestamp < timestamp) {
82 /* Same user@host: reverse logic. Whose idea was all this anyway? */
83 deletenick(np2);
84 } else {
85 deletenick(np);
86 return CMD_OK;
87 }
88 }
89 }
90 /* OK, we've survived the collision hazard. Change timestamp and rename */
91 np->timestamp=timestamp;
92 removenickfromhash(np);
93 strncpy(np->nick,cargv[0],NICKLEN);
94 np->nick[NICKLEN]='\0';
95 addnicktohash(np);
2fdef282 96 triggerhook(HOOK_NICK_RENAME,harg);
c86edd1d
Q
97 } else if (cargc>=8) { /* new nick */
98 /* Jupiler 2 1016645147 ~Jupiler www.iglobal.be +ir moo [FUTURE CRAP HERE] DV74O] BNBd7 :Jupiler */
99 timestamp=strtol(cargv[2],NULL,10);
100 np=getnickbynick(cargv[0]);
101 if (np!=NULL) {
102 /* Nick collision */
103 if (ircd_strcmp(np->ident,cargv[3]) || ircd_strcmp(np->host->name->content,cargv[4])) {
104 /* Different user@host */
105 if (timestamp>np->timestamp) {
106 /* New nick is newer. Ignore this nick message */
107 return CMD_OK;
108 } else {
109 /* New nick is older. Kill the imposter, and drop through */
110 deletenick(np);
111 }
112 } else {
113 if (timestamp>np->timestamp) {
114 /* Same user@host, newer timestamp: we're killing off a ghost */
115 deletenick(np);
116 } else {
117 /* This nick is the ghost, so ignore it */
118 return CMD_OK;
119 }
120 }
121 }
122
123 nh=gethandlebynumeric(numerictolong(cargv[cargc-2],5));
124 if (!nh) {
125 /* This isn't a valid numeric */
126 Error("nick",ERR_WARNING,"Received NICK with invalid numeric %s from %s.",cargv[cargc-2],sender);
127 return CMD_ERROR;
128 }
1a38afd3
P
129
130 base64toip(cargv[cargc-3], &ipaddress);
131 if (!irc_in_addr_valid(&ipaddress)) {
132 Error("nick",ERR_ERROR,"Received NICK with invalid ipaddress for %s from %s.",cargv[0],sender);
133 return CMD_ERROR;
134 }
135
c86edd1d
Q
136 /* At this stage the nick is cleared to proceed */
137 np=newnick();
138 strncpy(np->nick,cargv[0],NICKLEN);
139 np->nick[NICKLEN]='\0';
140 np->numeric=numerictolong(cargv[cargc-2],5);
141 strncpy(np->ident,cargv[3],USERLEN);
142 np->ident[USERLEN]='\0';
143 np->host=findorcreatehost(cargv[4]);
144 np->realname=findorcreaterealname(cargv[cargc-1]);
145 np->nextbyhost=np->host->nicks;
146 np->host->nicks=np;
147 np->nextbyrealname=np->realname->nicks;
148 np->realname->nicks=np;
149 np->timestamp=timestamp;
526e7c1d
P
150
151 base64toip(cargv[cargc-3], &ipaddress);
071d403d 152 np->ipnode = refnode(iptree, &ipaddress, PATRICIA_MAXBITS);
96644df6 153 node_increment_usercount(np->ipnode);
526e7c1d 154
5144ddc4 155 np->away=NULL;
c86edd1d
Q
156 np->shident=NULL;
157 np->sethost=NULL;
843184e3 158 np->opername=NULL;
c86edd1d
Q
159 np->umodes=0;
160 np->marker=0;
161 memset(np->exts, 0, MAXNICKEXTS * sizeof(void *));
3404a8f0 162 np->authname=NULLAUTHNAME;
47657339 163 np->auth=NULL;
c4ffdb9b 164 np->accountts=0;
c86edd1d 165 if(cargc>=9) {
843184e3
CP
166 int sethostarg = 6, opernamearg = 6, accountarg = 6;
167
c86edd1d 168 setflags(&(np->umodes),UMODE_ALL,cargv[5],umodeflags,REJECT_NONE);
843184e3
CP
169
170 if(IsOper(np) && (serverlist[myhub].flags & SMODE_OPERNAME)) {
171 accountarg++;
172 sethostarg++;
173
174 np->opername=getsstring(cargv[opernamearg],ACCOUNTLEN);
175 }
176
c86edd1d 177 if (IsAccount(np)) {
843184e3
CP
178 sethostarg++;
179
180 if ((accountts=strchr(cargv[accountarg],':'))) {
3404a8f0 181 userid=0;
c86edd1d 182 *accountts++='\0';
3294b10b 183 np->accountts=strtoul(accountts,&accountid,10);
47657339 184 if(accountid) {
c153c0dc 185 userid=strtoul(accountid + 1,&accountflags,10);
3294b10b 186 if(userid) {
843184e3 187 np->auth=findorcreateauthname(userid, cargv[accountarg]);
3294b10b 188 np->authname=np->auth->name;
47657339
C
189 np->auth->usercount++;
190 np->nextbyauthname=np->auth->nicks;
191 np->auth->nicks=np;
0b0fb773 192 if(accountflags)
92acf9ae 193 np->auth->flags=strtoull(accountflags + 1,NULL,10);
47657339 194 }
3404a8f0
CP
195 }
196 if(!userid) {
3294b10b
CP
197 np->authname=malloc(strlen(cargv[accountarg]) + 1);
198 strcpy(np->authname,cargv[accountarg]);
47657339 199 }
c86edd1d 200 }
c86edd1d 201 }
843184e3 202 if (IsSetHost(np) && (fakehost=strchr(cargv[sethostarg],'@'))) {
c86edd1d
Q
203 /* valid sethost */
204 *fakehost++='\0';
843184e3 205 np->shident=getsstring(cargv[sethostarg],USERLEN);
c86edd1d
Q
206 np->sethost=getsstring(fakehost,HOSTLEN);
207 }
208 }
209
210 /* Place this nick in the server nick table. Note that nh is valid from the numeric check above */
211 if (*nh) {
212 /* There was a nick there already -- we have a masked numeric collision
213 * This shouldn't happen, but if it does the newer nick takes precedence
214 * (the two nicks are from the same server, and if the server has reissued
215 * the masked numeric it must believe the old user no longer exists).
216 */
217 Error("nick",ERR_ERROR,"Masked numeric collision for %s [%s vs %s]",cargv[0],cargv[cargc-2],longtonumeric((*nh)->numeric,5));
218 deletenick(*nh);
219 }
220 *nh=np;
221
222 /* And the nick hash table */
223 addnicktohash(np);
224
225 /* Trigger the hook */
226 triggerhook(HOOK_NICK_NEWNICK,np);
227 } else {
228 Error("nick",ERR_WARNING,"Nick message with weird number of parameters (%d)",cargc);
229 }
230
231 return CMD_OK;
232}
233
234int handlequitmsg(void *source, int cargc, char **cargv) {
235 nick *np;
236 void *harg[2];
237
85c382d3 238 if (cargc>0) {
239 harg[1]=(void *)cargv[0];
c86edd1d
Q
240 } else {
241 harg[1]="";
242 }
243
244 np=getnickbynumericstr((char *)source);
245 if (np) {
246 harg[0]=(void *)np;
247 triggerhook(HOOK_NICK_QUIT, harg);
248 deletenick(np);
249 } else {
250 Error("nick",ERR_WARNING,"Quit from non-existant numeric %s",(char *)source);
251 }
252 return CMD_OK;
253}
254
255int handlekillmsg(void *source, int cargc, char **cargv) {
256 nick *np;
acd438c7 257 void *harg[2];
e3073692
CP
258#warning Fix me to use source
259
c86edd1d
Q
260 if (cargc<1) {
261 Error("nick",ERR_WARNING,"Kill message with too few parameters");
262 return CMD_ERROR;
263 }
acd438c7
CP
264
265 if (cargc>1) {
266 harg[1]=(void *)cargv[1];
267 } else {
268 harg[1]="";
269 }
270
c86edd1d
Q
271 np=getnickbynumericstr(cargv[0]);
272 if (np) {
acd438c7
CP
273 harg[0]=(void *)np;
274 triggerhook(HOOK_NICK_KILL, harg);
c86edd1d
Q
275 deletenick(np);
276 } else {
277 Error("nick",ERR_WARNING,"Kill for non-existant numeric %s",cargv[0]);
278 }
279 return CMD_OK;
280}
281
282int handleusermodemsg(void *source, int cargc, char **cargv) {
283 nick *np;
284 flag_t oldflags;
285 char *fakehost;
286
287 if (cargc<2) {
288 Error("nick",ERR_WARNING,"Mode message with too few parameters");
289 return CMD_LAST; /* With <2 params the channels module won't want it either */
290 }
291
292 if (cargv[0][0]=='#') {
293 /* Channel mode change, we don't care.
294 * We don't bother checking for other channel types here, since & channel mode
295 * changes aren't broadcast and + channels don't have mode changes :) */
296 return CMD_OK;
297 }
298
299 np=getnickbynumericstr((char *)source);
300 if (np!=NULL) {
301 if (ircd_strcmp(cargv[0],np->nick)) {
302 Error("nick",ERR_WARNING,"Attempted mode change on user %s by %s",cargv[0],np->nick);
303 return CMD_OK;
304 }
305 oldflags=np->umodes;
306 setflags(&(np->umodes),UMODE_ALL,cargv[1],umodeflags,REJECT_NONE);
843184e3
CP
307
308 if (strchr(cargv[1],'o')) { /* o always comes on its own when being set */
309 if(serverlist[myhub].flags & SMODE_OPERNAME) {
310 if((np->umodes & UMODE_OPER)) {
311 np->opername = getsstring(cargv[2], ACCOUNTLEN);
312 } else {
313 freesstring(np->opername);
1a0e3977 314 np->opername = NULL;
843184e3
CP
315 }
316 }
317 if((np->umodes ^ oldflags) & UMODE_OPER)
318 triggerhook(HOOK_NICK_MODEOPER,np);
319 }
c86edd1d
Q
320 if (strchr(cargv[1],'h')) { /* Have to allow +h twice.. */
321 /* +-h: just the freesstring() calls for the -h case */
322 freesstring(np->shident); /* freesstring(NULL) is OK */
323 freesstring(np->sethost);
324 np->shident=NULL;
325 np->sethost=NULL;
326 if (IsSetHost(np) && cargc>2) { /* +h and mask received */
327 if ((fakehost=strchr(cargv[2],'@'))) {
328 /* user@host change */
329 *fakehost++='\0';
330 np->shident=getsstring(cargv[2],USERLEN);
331 np->sethost=getsstring(fakehost,HOSTLEN);
332 } else {
333 np->sethost=getsstring(cargv[2],HOSTLEN);
334 }
335 }
336 triggerhook(HOOK_NICK_SETHOST, (void *)np);
337 }
338 } else {
339 Error("nick",ERR_WARNING,"Usermode change by unknown user %s",(char *)source);
340 }
341 return CMD_OK;
342}
343
344int handlewhoismsg(void *source, int cargc, char **cargv) {
345 nick *sender,*target;
346 nick *nicks[2];
347
348 if (cargc<2)
349 return CMD_OK;
350
351 if (strncmp(cargv[0],mynumeric->content,2)) {
352 return CMD_OK;
353 }
354
355 /* Find the sender... */
356 if ((sender=getnickbynumericstr((char *)source))==NULL) {
357 Error("localuser",ERR_WARNING,"WHOIS message from non existent numeric %s",(char *)source);
358 return CMD_OK;
359 }
360
361 /* :hub.splidge.netsplit.net 311 moo splidge splidge ground.stbarnab.as * :splidge
362 :hub.splidge.netsplit.net 312 moo splidge splidge.netsplit.net :splidge's netsplit leaf
363 :hub.splidge.netsplit.net 313 moo splidge :is an IRC Operator
364 :hub.splidge.netsplit.net 318 moo splidge :End of /WHOIS list.
365 */
366
367 /* And the target... */
368 if ((target=getnickbynick(cargv[1]))==NULL) {
369 irc_send(":%s 401 %s %s :No such nick",myserver->content,sender->nick,cargv[1]);
370 } else {
371 irc_send(":%s 311 %s %s %s %s * :%s",myserver->content,sender->nick,target->nick,target->ident,
372 target->host->name->content, target->realname->name->content);
373 nicks[0]=sender; nicks[1]=target;
374 triggerhook(HOOK_NICK_WHOISCHANNELS,nicks);
375 if (IsOper(sender) || !HIS_SERVER ) {
376 irc_send(":%s 312 %s %s %s :%s",myserver->content,sender->nick,target->nick,
377 serverlist[homeserver(target->numeric)].name->content,
378 serverlist[homeserver(target->numeric)].description->content);
379 } else {
380 irc_send(":%s 312 %s %s " HIS_SERVERNAME " :" HIS_SERVERDESC,myserver->content,sender->nick,target->nick);
381 }
382 if (IsOper(target)) {
383 irc_send(":%s 313 %s %s :is an IRC Operator",myserver->content,sender->nick,target->nick);
384 }
385 if (IsAccount(target)) {
109ba422 386 irc_send(":%s 330 %s %s %s :is authed as",myserver->content,sender->nick,target->nick,target->authname);
c86edd1d 387 }
109ba422 388 if (homeserver(target->numeric)==mylongnum && !IsService(target) && !IsHideIdle(target)) {
c86edd1d 389 irc_send(":%s 317 %s %s %ld %ld :seconds idle, signon time",myserver->content,sender->nick,target->nick,
80300a20 390 (getnettime() - target->timestamp) % (((target->numeric + target->timestamp) % 983) + 7),target->timestamp);
c86edd1d
Q
391 }
392 }
393
394 irc_send(":%s 318 %s %s :End of /WHOIS list.",myserver->content,sender->nick,cargv[1]);
395 return CMD_OK;
396}
397
398int handleaccountmsg(void *source, int cargc, char **cargv) {
399 nick *target;
47657339 400 unsigned long userid;
c4ffdb9b 401 time_t accountts;
6cb1b086 402 u_int64_t accountflags=0, oldflags;
c4ffdb9b
CP
403
404 if (cargc<4) {
c86edd1d
Q
405 return CMD_OK;
406 }
407
408 if ((target=getnickbynumericstr(cargv[0]))==NULL) {
409 return CMD_OK;
410 }
411
c4ffdb9b
CP
412 accountts=strtoul(cargv[2],NULL,10);
413 userid=strtoul(cargv[3],NULL,10);
3294b10b 414 if(cargc>=5)
92acf9ae 415 accountflags=strtoull(cargv[4],NULL,10);
c4ffdb9b
CP
416
417 /* allow user flags to change if all fields match */
c86edd1d 418 if (IsAccount(target)) {
c4ffdb9b
CP
419 void *arg[2];
420
3294b10b 421 if (!target->auth || strcmp(target->auth->name,cargv[1]) || (target->auth->userid != userid) || (target->accountts != accountts)) {
c4ffdb9b
CP
422 return CMD_OK;
423 }
424
92acf9ae
CP
425 oldflags = target->auth->flags;
426 arg[0] = target->auth;
427 arg[1] = &oldflags;
c4ffdb9b
CP
428
429 if (cargc>=5)
430 target->auth->flags=accountflags;
431
432 triggerhook(HOOK_AUTH_FLAGSUPDATED, (void *)arg);
433
c86edd1d
Q
434 return CMD_OK;
435 }
436
437 SetAccount(target);
c4ffdb9b
CP
438 target->accountts=accountts;
439
440 if(!userid) {
47657339 441 target->auth=NULL;
3294b10b
CP
442 target->authname=malloc(strlen(cargv[1]) + 1);
443 strcpy(target->authname,cargv[1]);
c4ffdb9b 444 } else {
5bfb1d14 445 target->auth=findorcreateauthname(userid, cargv[1]);
c4ffdb9b 446 target->auth->usercount++;
3294b10b 447 target->authname=target->auth->name;
c4ffdb9b
CP
448 target->nextbyauthname = target->auth->nicks;
449 target->auth->nicks = target;
450 if (cargc>=5)
451 target->auth->flags=accountflags;
47657339 452 }
c4ffdb9b 453
c86edd1d 454 triggerhook(HOOK_NICK_ACCOUNT, (void *)target);
526e7c1d 455
c86edd1d
Q
456 return CMD_OK;
457}
458
459int handlestatsmsg(void *source, int cargc, char **cargv) {
460 int sourceserver;
461 char *sender=(char *)source;
462 char *replytarget;
463 char *fromstring;
464 nick *np;
465
466 if (cargc<2) {
467 Error("nick",ERR_WARNING,"STATS request without enough parameters!");
468 return CMD_OK;
469 }
470
471 if (strlen(sender)==5) {
472 /* client */
473 np=getnickbynumericstr(sender);
474 if (!np) {
475 Error("nick",ERR_WARNING,"STATS request from unknown client %s",sender);
476 return CMD_OK;
477 }
478 replytarget=np->nick;
479 } else {
480 Error("nick",ERR_WARNING,"STATS request from odd source %s",sender);
481 return CMD_OK;
482 }
483
484 /* Reply to stats for ANY server.. including any we are juping */
485 sourceserver=numerictolong(cargv[1],2);
486 if (serverlist[sourceserver].maxusernum==0) {
487 Error("nick",ERR_WARNING,"Stats request for bad server %s",cargv[1]);
488 return CMD_OK;
489 }
490 fromstring=serverlist[sourceserver].name->content;
491
492 switch(cargv[0][0]) {
493 case 'u':
494 irc_send(":%s 242 %s :Server Up %s",fromstring,replytarget,
495 longtoduration(time(NULL)-starttime, 0));
496 irc_send(":%s 250 %s :Highest connection count: 10 (9 clients)",fromstring,replytarget);
497 break;
498
499 case 'P':
500 irc_send(":%s 217 %s P none 0 :0x2000",fromstring,replytarget);
501 break;
09285ac1 502
503 case 'm':
504 stats_m(fromstring, replytarget);
505 break;
c86edd1d
Q
506 }
507
508 irc_send(":%s 219 %s %c :End of /STATS report",fromstring,replytarget,cargv[0][0]);
509
510 return CMD_OK;
511}
6885ae9c
CP
512
513int handleprivmsg(void *source, int cargc, char **cargv) {
514 nick *sender;
515 char *message;
516 void *args[3];
517
518 if (cargc<2)
519 return CMD_OK;
520
521 if (cargv[0][0]!='$')
522 return CMD_OK;
523
524 sender=getnickbynumericstr((char *)source);
525
526 if (!match2strings(cargv[0] + 1,myserver->content))
527 return CMD_OK;
528
529 message=cargv[0];
530
531 args[0]=sender;
532 args[1]=cargv[0];
533 args[2]=cargv[1];
534
535 triggerhook(HOOK_NICK_MASKPRIVMSG, (void *)args);
536
537 return CMD_OK;
538}
539
5144ddc4 540int handleawaymsg(void *source, int cargc, char **cargv) {
541 nick *sender;
542
543 /* Check source is a valid user */
544 if (!(sender=getnickbynumericstr(source))) {
545 return CMD_OK;
546 }
547
548 /* Done with the old away message either way */
549 freesstring(sender->away);
550 sender->away=NULL;
551
552 /* If we have an arg and it isn't an empty string, this sets a new message */
553 if (cargc > 0 && *(cargv[0])) {
554 sender->away=getsstring(cargv[0], AWAYLEN);
555 }
556
557 return CMD_OK;
558}