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