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