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