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