]> jfr.im git - irc/quakenet/newserv.git/blob - nick/nickhandlers.c
Began manual merge.
[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 if (strchr(cargv[1],'o')) {
243 void *harg[2];
244 harg[0]=np;
245 harg[1]=cargv[1];
246 triggerhook(HOOK_NICK_MODEOPER,harg);
247 }
248 setflags(&(np->umodes),UMODE_ALL,cargv[1],umodeflags,REJECT_NONE);
249 if (strchr(cargv[1],'h')) { /* Have to allow +h twice.. */
250 /* +-h: just the freesstring() calls for the -h case */
251 freesstring(np->shident); /* freesstring(NULL) is OK */
252 freesstring(np->sethost);
253 np->shident=NULL;
254 np->sethost=NULL;
255 if (IsSetHost(np) && cargc>2) { /* +h and mask received */
256 if ((fakehost=strchr(cargv[2],'@'))) {
257 /* user@host change */
258 *fakehost++='\0';
259 np->shident=getsstring(cargv[2],USERLEN);
260 np->sethost=getsstring(fakehost,HOSTLEN);
261 } else {
262 np->sethost=getsstring(cargv[2],HOSTLEN);
263 }
264 }
265 triggerhook(HOOK_NICK_SETHOST, (void *)np);
266 }
267 } else {
268 Error("nick",ERR_WARNING,"Usermode change by unknown user %s",(char *)source);
269 }
270 return CMD_OK;
271 }
272
273 int handlewhoismsg(void *source, int cargc, char **cargv) {
274 nick *sender,*target;
275 nick *nicks[2];
276
277 if (cargc<2)
278 return CMD_OK;
279
280 if (strncmp(cargv[0],mynumeric->content,2)) {
281 return CMD_OK;
282 }
283
284 /* Find the sender... */
285 if ((sender=getnickbynumericstr((char *)source))==NULL) {
286 Error("localuser",ERR_WARNING,"WHOIS message from non existent numeric %s",(char *)source);
287 return CMD_OK;
288 }
289
290 /* :hub.splidge.netsplit.net 311 moo splidge splidge ground.stbarnab.as * :splidge
291 :hub.splidge.netsplit.net 312 moo splidge splidge.netsplit.net :splidge's netsplit leaf
292 :hub.splidge.netsplit.net 313 moo splidge :is an IRC Operator
293 :hub.splidge.netsplit.net 318 moo splidge :End of /WHOIS list.
294 */
295
296 /* And the target... */
297 if ((target=getnickbynick(cargv[1]))==NULL) {
298 irc_send(":%s 401 %s %s :No such nick",myserver->content,sender->nick,cargv[1]);
299 } else {
300 irc_send(":%s 311 %s %s %s %s * :%s",myserver->content,sender->nick,target->nick,target->ident,
301 target->host->name->content, target->realname->name->content);
302 nicks[0]=sender; nicks[1]=target;
303 triggerhook(HOOK_NICK_WHOISCHANNELS,nicks);
304 if (IsOper(sender) || !HIS_SERVER ) {
305 irc_send(":%s 312 %s %s %s :%s",myserver->content,sender->nick,target->nick,
306 serverlist[homeserver(target->numeric)].name->content,
307 serverlist[homeserver(target->numeric)].description->content);
308 } else {
309 irc_send(":%s 312 %s %s " HIS_SERVERNAME " :" HIS_SERVERDESC,myserver->content,sender->nick,target->nick);
310 }
311 if (IsOper(target)) {
312 irc_send(":%s 313 %s %s :is an IRC Operator",myserver->content,sender->nick,target->nick);
313 }
314 if (IsAccount(target)) {
315 irc_send(":%s 330 %s %s %s :is logged in as",myserver->content,sender->nick,target->nick,target->authname);
316 }
317 if (homeserver(target->numeric)==mylongnum && !IsService(target)) {
318 irc_send(":%s 317 %s %s %ld %ld :seconds idle, signon time",myserver->content,sender->nick,target->nick,
319 0,target->timestamp);
320 }
321 }
322
323 irc_send(":%s 318 %s %s :End of /WHOIS list.",myserver->content,sender->nick,cargv[1]);
324 return CMD_OK;
325 }
326
327 int handleaccountmsg(void *source, int cargc, char **cargv) {
328 nick *target;
329
330 if (cargc<2) {
331 return CMD_OK;
332 }
333
334 if ((target=getnickbynumericstr(cargv[0]))==NULL) {
335 return CMD_OK;
336 }
337
338 if (IsAccount(target)) {
339 return CMD_OK;
340 }
341
342 SetAccount(target);
343 strncpy(target->authname,cargv[1],ACCOUNTLEN);
344 target->authname[ACCOUNTLEN]='\0';
345
346 triggerhook(HOOK_NICK_ACCOUNT, (void *)target);
347
348 return CMD_OK;
349 }
350
351 int handlestatsmsg(void *source, int cargc, char **cargv) {
352 int sourceserver;
353 char *sender=(char *)source;
354 char *replytarget;
355 char *fromstring;
356 nick *np;
357
358 if (cargc<2) {
359 Error("nick",ERR_WARNING,"STATS request without enough parameters!");
360 return CMD_OK;
361 }
362
363 if (strlen(sender)==5) {
364 /* client */
365 np=getnickbynumericstr(sender);
366 if (!np) {
367 Error("nick",ERR_WARNING,"STATS request from unknown client %s",sender);
368 return CMD_OK;
369 }
370 replytarget=np->nick;
371 } else {
372 Error("nick",ERR_WARNING,"STATS request from odd source %s",sender);
373 return CMD_OK;
374 }
375
376 /* Reply to stats for ANY server.. including any we are juping */
377 sourceserver=numerictolong(cargv[1],2);
378 if (serverlist[sourceserver].maxusernum==0) {
379 Error("nick",ERR_WARNING,"Stats request for bad server %s",cargv[1]);
380 return CMD_OK;
381 }
382 fromstring=serverlist[sourceserver].name->content;
383
384 switch(cargv[0][0]) {
385 case 'u':
386 irc_send(":%s 242 %s :Server Up %s",fromstring,replytarget,
387 longtoduration(time(NULL)-starttime, 0));
388 irc_send(":%s 250 %s :Highest connection count: 10 (9 clients)",fromstring,replytarget);
389 break;
390
391 case 'P':
392 irc_send(":%s 217 %s P none 0 :0x2000",fromstring,replytarget);
393 break;
394
395 }
396
397 irc_send(":%s 219 %s %c :End of /STATS report",fromstring,replytarget,cargv[0][0]);
398
399 return CMD_OK;
400 }