Annotation of ircnowd/src/ngircd/irc-login.c, Revision 1.1.1.1
1.1 tomglok 1: /*
2: * ngIRCd -- The Next Generation IRC Daemon
3: * Copyright (c)2001-2018 Alexander Barton (alex@barton.de) and Contributors.
4: *
5: * This program is free software; you can redistribute it and/or modify
6: * it under the terms of the GNU General Public License as published by
7: * the Free Software Foundation; either version 2 of the License, or
8: * (at your option) any later version.
9: * Please read the file COPYING, README and AUTHORS for more information.
10: */
11:
12: #include "portab.h"
13:
14: /**
15: * @file
16: * Login and logout
17: */
18:
19: #include <assert.h>
20: #include <ctype.h>
21: #include <stdlib.h>
22: #include <string.h>
23: #include <strings.h>
24: #include <time.h>
25:
26: #include "conn-func.h"
27: #include "conf.h"
28: #include "channel.h"
29: #include "log.h"
30: #include "login.h"
31: #include "messages.h"
32: #include "parse.h"
33: #include "irc.h"
34: #include "irc-macros.h"
35: #include "irc-write.h"
36:
37: #include "irc-login.h"
38:
39: static void Change_Nick PARAMS((CLIENT * Origin, CLIENT * Target, char *NewNick,
40: bool InformClient));
41:
42: /**
43: * Handler for the IRC "PASS" command.
44: *
45: * @param Client The client from which this command has been received.
46: * @param Req Request structure with prefix and all parameters.
47: * @return CONNECTED or DISCONNECTED.
48: */
49: GLOBAL bool
50: IRC_PASS( CLIENT *Client, REQUEST *Req )
51: {
52: char *type, *orig_flags;
53: int protohigh, protolow;
54:
55: assert( Client != NULL );
56: assert( Req != NULL );
57:
58: /* Return an error if this is not a local client */
59: if (Client_Conn(Client) <= NONE)
60: return IRC_WriteErrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
61: Client_ID(Client), Req->command);
62:
63: if (Client_Type(Client) == CLIENT_UNKNOWN && Req->argc == 1) {
64: /* Not yet registered "unknown" connection, PASS with one
65: * argument: either a regular client, service, or server
66: * using the old RFC 1459 section 4.1.1 syntax. */
67: LogDebug("Connection %d: got PASS command (RFC 1459) ...",
68: Client_Conn(Client));
69: } else if ((Client_Type(Client) == CLIENT_UNKNOWN ||
70: Client_Type(Client) == CLIENT_UNKNOWNSERVER) &&
71: (Req->argc == 3 || Req->argc == 4)) {
72: /* Not yet registered "unknown" connection or outgoing server
73: * link, PASS with three or four argument: server using the
74: * RFC 2813 section 4.1.1 syntax. */
75: LogDebug("Connection %d: got PASS command (RFC 2813, new server link) ...",
76: Client_Conn(Client));
77: } else if (Client_Type(Client) == CLIENT_UNKNOWN ||
78: Client_Type(Client) == CLIENT_UNKNOWNSERVER) {
79: /* Unregistered connection, but wrong number of arguments: */
80: return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
81: Client_ID(Client), Req->command);
82: } else {
83: /* Registered connection, PASS command is not allowed! */
84: return IRC_WriteErrClient(Client, ERR_ALREADYREGISTRED_MSG,
85: Client_ID(Client));
86: }
87:
88: Conn_SetPassword(Client_Conn(Client), Req->argv[0]);
89:
90: /* Protocol version */
91: if (Req->argc >= 2 && strlen(Req->argv[1]) >= 4) {
92: char c2, c4;
93:
94: c2 = Req->argv[1][2];
95: c4 = Req->argv[1][4];
96:
97: Req->argv[1][4] = '\0';
98: protolow = atoi(&Req->argv[1][2]);
99: Req->argv[1][2] = '\0';
100: protohigh = atoi(Req->argv[1]);
101:
102: Req->argv[1][2] = c2;
103: Req->argv[1][4] = c4;
104:
105: Client_SetType(Client, CLIENT_GOTPASS_2813);
106: } else {
107: protohigh = protolow = 0;
108: Client_SetType(Client, CLIENT_GOTPASS);
109: }
110:
111: /* Protocol type, see doc/Protocol.txt */
112: if (Req->argc >= 2 && strlen(Req->argv[1]) > 4)
113: type = &Req->argv[1][4];
114: else
115: type = NULL;
116:
117: /* Protocol flags/options */
118: if (Req->argc >= 4)
119: orig_flags = Req->argv[3];
120: else
121: orig_flags = "";
122:
123: /* Implementation, version and IRC+ flags */
124: if (Req->argc >= 3) {
125: char *impl, *ptr, *serverver, *flags;
126:
127: impl = Req->argv[2];
128: ptr = strchr(impl, '|');
129: if (ptr)
130: *ptr = '\0';
131:
132: if (type && strcmp(type, PROTOIRCPLUS) == 0) {
133: /* The peer seems to be a server which supports the
134: * IRC+ protocol (see doc/Protocol.txt). */
135: serverver = ptr ? ptr + 1 : "?";
136: flags = strchr(ptr ? serverver : impl, ':');
137: if (flags) {
138: *flags = '\0';
139: flags++;
140: } else
141: flags = "";
142: Log(LOG_INFO,
143: "Peer on connection %d announces itself as %s-%s using protocol %d.%d/IRC+ (flags: \"%s\").",
144: Client_Conn(Client), impl, serverver,
145: protohigh, protolow, flags);
146: } else {
147: /* The peer seems to be a server supporting the
148: * "original" IRC protocol (RFC 2813). */
149: if (strchr(orig_flags, 'Z'))
150: flags = "Z";
151: else
152: flags = "";
153: Log(LOG_INFO,
154: "Peer on connection %d announces itself as \"%s\" using protocol %d.%d (flags: \"%s\").",
155: Client_Conn(Client), impl,
156: protohigh, protolow, flags);
157: }
158: Client_SetFlags(Client, flags);
159: }
160:
161: return CONNECTED;
162: } /* IRC_PASS */
163:
164: /**
165: * Handler for the IRC "NICK" command.
166: *
167: * @param Client The client from which this command has been received.
168: * @param Req Request structure with prefix and all parameters.
169: * @return CONNECTED or DISCONNECTED.
170: */
171: GLOBAL bool
172: IRC_NICK( CLIENT *Client, REQUEST *Req )
173: {
174: CLIENT *intr_c, *target, *c;
175: CHANNEL *chan;
176: char *nick, *user, *hostname, *modes, *info;
177: int token, hops;
178:
179: assert( Client != NULL );
180: assert( Req != NULL );
181:
182: /* Some IRC clients, for example BitchX, send the NICK and USER
183: * commands in the wrong order ... */
184: if(Client_Type(Client) == CLIENT_UNKNOWN
185: || Client_Type(Client) == CLIENT_GOTPASS
186: || Client_Type(Client) == CLIENT_GOTNICK
187: #ifndef STRICT_RFC
188: || Client_Type(Client) == CLIENT_GOTUSER
189: #endif
190: || Client_Type(Client) == CLIENT_USER
191: || Client_Type(Client) == CLIENT_SERVICE
192: || (Client_Type(Client) == CLIENT_SERVER && Req->argc == 1))
193: {
194: /* User registration or change of nickname */
195: _IRC_ARGC_EQ_OR_RETURN_(Client, Req, 1)
196:
197: /* Search "target" client */
198: if (Client_Type(Client) == CLIENT_SERVER) {
199: _IRC_REQUIRE_PREFIX_OR_RETURN_(Client, Req)
200: target = Client_Search(Req->prefix);
201: if (!target)
202: return IRC_WriteErrClient(Client,
203: ERR_NOSUCHNICK_MSG,
204: Client_ID(Client),
205: Req->argv[0]);
206: } else {
207: /* Is this a restricted client? */
208: if (Client_HasMode(Client, 'r'))
209: return IRC_WriteErrClient(Client,
210: ERR_RESTRICTED_MSG,
211: Client_ID(Client));
212: target = Client;
213: }
214:
215: #ifndef STRICT_RFC
216: /* If the clients tries to change to its own nickname we won't
217: * do anything. This is how the original ircd behaves and some
218: * clients (for example Snak) expect it to be like this.
219: * But I doubt that this is "really the right thing" ... */
220: if (strcmp(Client_ID(target), Req->argv[0]) == 0)
221: return CONNECTED;
222: #endif
223:
224: /* Check that the new nickname is available. Special case:
225: * the client only changes from/to upper to lower case. */
226: if (strcasecmp(Client_ID(target), Req->argv[0]) != 0) {
227: if (!Client_CheckNick(target, Req->argv[0]))
228: return CONNECTED;
229: }
230:
231: if (Client_Type(target) != CLIENT_USER &&
232: Client_Type(target) != CLIENT_SERVICE &&
233: Client_Type(target) != CLIENT_SERVER) {
234: /* New client */
235: LogDebug("Connection %d: got valid NICK command ...",
236: Client_Conn( Client ));
237:
238: /* Register new nickname of this client */
239: Client_SetID( target, Req->argv[0] );
240:
241: #ifndef STRICT_RFC
242: if (Conf_AuthPing) {
243: #ifdef HAVE_ARC4RANDOM
244: Conn_SetAuthPing(Client_Conn(Client), arc4random());
245: #else
246: Conn_SetAuthPing(Client_Conn(Client), rand());
247: #endif
248: Conn_WriteStr(Client_Conn(Client), "PING :%ld",
249: Conn_GetAuthPing(Client_Conn(Client)));
250: LogDebug("Connection %d: sent AUTH PING %ld ...",
251: Client_Conn(Client),
252: Conn_GetAuthPing(Client_Conn(Client)));
253: }
254: #endif
255:
256: /* If we received a valid USER command already then
257: * register the new client! */
258: if( Client_Type( Client ) == CLIENT_GOTUSER )
259: return Login_User( Client );
260: else
261: Client_SetType( Client, CLIENT_GOTNICK );
262: } else {
263: /* Nickname change */
264:
265: /* Check that the user isn't on any channels set +N */
266: if(Client_Type(Client) == CLIENT_USER &&
267: !Client_HasMode(Client, 'o')) {
268: chan = Channel_First();
269: while (chan) {
270: if(Channel_HasMode(chan, 'N') &&
271: Channel_IsMemberOf(chan, Client))
272: return IRC_WriteErrClient(Client,
273: ERR_NONICKCHANGE_MSG,
274: Client_ID(Client),
275: Channel_Name(chan));
276: chan = Channel_Next(chan);
277: }
278: }
279:
280: Change_Nick(Client, target, Req->argv[0],
281: Client_Type(Client) == CLIENT_USER ? true : false);
282: IRC_SetPenalty(target, 2);
283: }
284:
285: return CONNECTED;
286: } else if(Client_Type(Client) == CLIENT_SERVER ||
287: Client_Type(Client) == CLIENT_SERVICE) {
288: /* Server or service introduces new client */
289:
290: /* Bad number of parameters? */
291: if (Req->argc != 2 && Req->argc != 7)
292: return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
293: Client_ID(Client), Req->command);
294:
295: if (Req->argc >= 7) {
296: /* RFC 2813 compatible syntax */
297: nick = Req->argv[0];
298: hops = atoi(Req->argv[1]);
299: user = Req->argv[2];
300: hostname = Req->argv[3];
301: token = atoi(Req->argv[4]);
302: modes = Req->argv[5] + 1;
303: info = Req->argv[6];
304: } else {
305: /* RFC 1459 compatible syntax */
306: nick = Req->argv[0];
307: hops = 1;
308: user = Req->argv[0];
309: hostname = Client_ID(Client);
310: token = atoi(Req->argv[1]);
311: modes = "";
312: info = Req->argv[0];
313: }
314:
315: c = Client_Search(nick);
316: if(c) {
317: /*
318: * the new nick is already present on this server:
319: * the new and the old one have to be disconnected now.
320: */
321: Log(LOG_ERR,
322: "Server %s introduces already registered nick \"%s\"!",
323: Client_ID(Client), Req->argv[0]);
324: return IRC_KillClient(Client, NULL, Req->argv[0],
325: "Nick collision");
326: }
327:
328: /* Find the Server this client is connected to */
329: intr_c = Client_GetFromToken(Client, token);
330: if (!intr_c) {
331: Log(LOG_ERR,
332: "Server %s introduces nick \"%s\" on unknown server!?",
333: Client_ID(Client), Req->argv[0]);
334: return IRC_KillClient(Client, NULL, Req->argv[0],
335: "Unknown server");
336: }
337:
338: c = Client_NewRemoteUser(intr_c, nick, hops, user, hostname,
339: token, modes, info, true);
340: if (!c) {
341: /* Out of memory, we need to disconnect client to keep
342: * network state consistent! */
343: Log(LOG_ALERT,
344: "Can't create client structure! (on connection %d)",
345: Client_Conn(Client));
346: return IRC_KillClient(Client, NULL, Req->argv[0],
347: "Server error");
348: }
349:
350: /* RFC 2813: client is now fully registered, inform all the
351: * other servers about the new user.
352: * RFC 1459: announce the new client only after receiving the
353: * USER command, first we need more information! */
354: if (Req->argc < 7) {
355: LogDebug("Client \"%s\" is being registered (RFC 1459) ...",
356: Client_Mask(c));
357: Client_SetType(c, CLIENT_GOTNICK);
358: } else
359: Client_Introduce(Client, c, CLIENT_USER);
360:
361: return CONNECTED;
362: }
363: else
364: return IRC_WriteErrClient(Client, ERR_ALREADYREGISTRED_MSG,
365: Client_ID(Client));
366: } /* IRC_NICK */
367:
368: /**
369: * Handler for the IRC "SVSNICK" command.
370: *
371: * @param Client The client from which this command has been received.
372: * @param Req Request structure with prefix and all parameters.
373: * @return CONNECTED or DISCONNECTED.
374: */
375: GLOBAL bool
376: IRC_SVSNICK(CLIENT *Client, REQUEST *Req)
377: {
378: CLIENT *from, *target;
379:
380: assert(Client != NULL);
381: assert(Req != NULL);
382:
383: _IRC_REQUIRE_PREFIX_OR_RETURN_(Client, Req)
384:
385: /* Search the originator */
386: from = Client_Search(Req->prefix);
387: if (!from)
388: from = Client;
389:
390: /* Search the target */
391: target = Client_Search(Req->argv[0]);
392: if (!target || Client_Type(target) != CLIENT_USER)
393: return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
394: Client_ID(Client), Req->argv[0]);
395:
396: if (Client_Conn(target) <= NONE) {
397: /* We have to forward the message to the server handling
398: * this user; this is required to make sure all servers
399: * in the network do follow the nick name change! */
400: return IRC_WriteStrClientPrefix(Client_NextHop(target), from,
401: "SVSNICK %s %s",
402: Req->argv[0], Req->argv[1]);
403: }
404:
405: /* Make sure that the new nickname is valid */
406: if (!Client_CheckNick(from, Req->argv[1]))
407: return CONNECTED;
408:
409: Change_Nick(from, target, Req->argv[1], true);
410: return CONNECTED;
411: }
412:
413: /**
414: * Handler for the IRC "USER" command.
415: *
416: * @param Client The client from which this command has been received.
417: * @param Req Request structure with prefix and all parameters.
418: * @return CONNECTED or DISCONNECTED.
419: */
420: GLOBAL bool
421: IRC_USER(CLIENT * Client, REQUEST * Req)
422: {
423: CLIENT *c;
424: char *ptr;
425:
426: assert(Client != NULL);
427: assert(Req != NULL);
428:
429: if (Client_Type(Client) == CLIENT_GOTNICK ||
430: #ifndef STRICT_RFC
431: Client_Type(Client) == CLIENT_UNKNOWN ||
432: #endif
433: Client_Type(Client) == CLIENT_GOTPASS)
434: {
435: /* New connection */
436: _IRC_ARGC_EQ_OR_RETURN_(Client, Req, 4)
437:
438: /* User name: only alphanumeric characters and limited
439: punctuation is allowed.*/
440: ptr = Req->argv[0];
441: while (*ptr) {
442: if (!isalnum((int)*ptr) &&
443: *ptr != '+' && *ptr != '-' && *ptr != '@' &&
444: *ptr != '.' && *ptr != '_') {
445: Conn_Close(Client_Conn(Client), NULL,
446: "Invalid user name", true);
447: return DISCONNECTED;
448: }
449: ptr++;
450: }
451:
452: /* Save the received username for authentication, and use
453: * it up to the first '@' as default user name (like ircd2.11,
454: * bahamut, ircd-seven, ...), prefixed with '~', if needed: */
455: Client_SetOrigUser(Client, Req->argv[0]);
456: ptr = strchr(Req->argv[0], '@');
457: if (ptr)
458: *ptr = '\0';
459: #ifdef IDENTAUTH
460: ptr = Client_User(Client);
461: if (!ptr || !*ptr || *ptr == '~')
462: Client_SetUser(Client, Req->argv[0], false);
463: #else
464: Client_SetUser(Client, Req->argv[0], false);
465: #endif
466:
467: /* "Real name" or user info text: Don't set it to the empty
468: * string, the original ircd can't deal with such "real names"
469: * (e. g. "USER user * * :") ... */
470: if (*Req->argv[3])
471: Client_SetInfo(Client, Req->argv[3]);
472: else
473: Client_SetInfo(Client, "-");
474:
475: LogDebug("Connection %d: got valid USER command ...",
476: Client_Conn(Client));
477: if (Client_Type(Client) == CLIENT_GOTNICK)
478: return Login_User(Client);
479: else
480: Client_SetType(Client, CLIENT_GOTUSER);
481: return CONNECTED;
482:
483: } else if (Client_Type(Client) == CLIENT_SERVER ||
484: Client_Type(Client) == CLIENT_SERVICE) {
485: /* Server/service updating an user */
486: _IRC_ARGC_EQ_OR_RETURN_(Client, Req, 4)
487: _IRC_REQUIRE_PREFIX_OR_RETURN_(Client, Req)
488:
489: c = Client_Search(Req->prefix);
490: if (!c)
491: return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
492: Client_ID(Client),
493: Req->prefix);
494:
495: Client_SetUser(c, Req->argv[0], true);
496: Client_SetOrigUser(c, Req->argv[0]);
497: Client_SetHostname(c, Req->argv[1]);
498: Client_SetInfo(c, Req->argv[3]);
499:
500: LogDebug("Connection %d: got valid USER command for \"%s\".",
501: Client_Conn(Client), Client_Mask(c));
502:
503: /* RFC 1459 style user registration?
504: * Introduce client to network: */
505: if (Client_Type(c) == CLIENT_GOTNICK)
506: Client_Introduce(Client, c, CLIENT_USER);
507:
508: return CONNECTED;
509: } else if (Client_Type(Client) == CLIENT_USER) {
510: /* Already registered connection */
511: return IRC_WriteErrClient(Client, ERR_ALREADYREGISTRED_MSG,
512: Client_ID(Client));
513: } else {
514: /* Unexpected/invalid connection state? */
515: return IRC_WriteErrClient(Client, ERR_NOTREGISTERED_MSG,
516: Client_ID(Client));
517: }
518: } /* IRC_USER */
519:
520: /**
521: * Handler for the IRC "SERVICE" command.
522: *
523: * At the moment ngIRCd doesn't support directly linked services, so this
524: * function returns ERR_ERRONEUSNICKNAME when the SERVICE command has not been
525: * received from a peer server.
526: *
527: * @param Client The client from which this command has been received.
528: * @param Req Request structure with prefix and all parameters.
529: * @return CONNECTED or DISCONNECTED.
530: */
531: GLOBAL bool
532: IRC_SERVICE(CLIENT *Client, REQUEST *Req)
533: {
534: CLIENT *c, *intr_c;
535: char *nick, *user, *host, *info, *modes, *ptr;
536: int token, hops;
537:
538: assert(Client != NULL);
539: assert(Req != NULL);
540:
541: if (Client_Type(Client) != CLIENT_GOTPASS &&
542: Client_Type(Client) != CLIENT_SERVER)
543: return IRC_WriteErrClient(Client, ERR_ALREADYREGISTRED_MSG,
544: Client_ID(Client));
545:
546: if (Client_Type(Client) != CLIENT_SERVER)
547: return IRC_WriteErrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
548: Client_ID(Client), Req->argv[0]);
549:
550: nick = Req->argv[0];
551: user = NULL; host = NULL;
552: token = atoi(Req->argv[1]);
553: hops = atoi(Req->argv[4]);
554: info = Req->argv[5];
555:
556: /* Validate service name ("nickname") */
557: c = Client_Search(nick);
558: if(c) {
559: /* Nickname collision: disconnect (KILL) both clients! */
560: Log(LOG_ERR,
561: "Server %s introduces already registered service \"%s\"!",
562: Client_ID(Client), nick);
563: return IRC_KillClient(Client, NULL, nick, "Nick collision");
564: }
565:
566: /* Get the server to which the service is connected */
567: intr_c = Client_GetFromToken(Client, token);
568: if (! intr_c) {
569: Log(LOG_ERR,
570: "Server %s introduces service \"%s\" on unknown server!?",
571: Client_ID(Client), nick);
572: return IRC_KillClient(Client, NULL, nick, "Unknown server");
573: }
574:
575: /* Get user and host name */
576: ptr = strchr(nick, '@');
577: if (ptr) {
578: *ptr = '\0';
579: host = ++ptr;
580: }
581: if (!host)
582: host = Client_Hostname(intr_c);
583: ptr = strchr(nick, '!');
584: if (ptr) {
585: *ptr = '\0';
586: user = ++ptr;
587: }
588: if (!user)
589: user = nick;
590:
591: /* According to RFC 2812/2813 parameter 4 <type> "is currently reserved
592: * for future usage"; but we use it to transfer the modes and check
593: * that the first character is a '+' sign and ignore it otherwise. */
594: modes = (Req->argv[3][0] == '+') ? ++Req->argv[3] : "";
595:
596: c = Client_NewRemoteUser(intr_c, nick, hops, user, host,
597: token, modes, info, true);
598: if (! c) {
599: /* Couldn't create client structure, so KILL the service to
600: * keep network status consistent ... */
601: Log(LOG_ALERT,
602: "Can't create client structure! (on connection %d)",
603: Client_Conn(Client));
604: return IRC_KillClient(Client, NULL, nick, "Server error");
605: }
606:
607: Client_Introduce(Client, c, CLIENT_SERVICE);
608: return CONNECTED;
609: } /* IRC_SERVICE */
610:
611: /**
612: * Handler for the IRC "WEBIRC" command.
613: *
614: * @param Client The client from which this command has been received.
615: * @param Req Request structure with prefix and all parameters.
616: * @return CONNECTED or DISCONNECTED.
617: */
618: GLOBAL bool
619: IRC_WEBIRC(CLIENT *Client, REQUEST *Req)
620: {
621: if (!Conf_WebircPwd[0] || strcmp(Req->argv[0], Conf_WebircPwd) != 0)
622: return IRC_WriteErrClient(Client, ERR_PASSWDMISMATCH_MSG,
623: Client_ID(Client));
624:
625: LogDebug("Connection %d: got valid WEBIRC command: user=%s, host=%s, ip=%s",
626: Client_Conn(Client), Req->argv[1], Req->argv[2], Req->argv[3]);
627:
628: Client_SetUser(Client, Req->argv[1], true);
629: Client_SetOrigUser(Client, Req->argv[1]);
630: if (Conf_DNS)
631: Client_SetHostname(Client, Req->argv[2]);
632: else
633: Client_SetHostname(Client, Req->argv[3]);
634: Client_SetIPAText(Client, Req->argv[3]);
635:
636: return CONNECTED;
637: } /* IRC_WEBIRC */
638:
639: /**
640: * Handler for the IRC "QUIT" command.
641: *
642: * @param Client The client from which this command has been received.
643: * @param Req Request structure with prefix and all parameters.
644: * @return CONNECTED or DISCONNECTED.
645: */
646: GLOBAL bool
647: IRC_QUIT( CLIENT *Client, REQUEST *Req )
648: {
649: CLIENT *target;
650: char quitmsg[COMMAND_LEN];
651:
652: assert(Client != NULL);
653: assert(Req != NULL);
654:
655: if (Req->argc == 1)
656: strlcpy(quitmsg, Req->argv[0], sizeof quitmsg);
657:
658: if (Client_Type(Client) == CLIENT_SERVER) {
659: /* Server */
660: _IRC_REQUIRE_PREFIX_OR_RETURN_(Client, Req)
661:
662: target = Client_Search(Req->prefix);
663: if (!target) {
664: Log(LOG_WARNING,
665: "Got QUIT from %s for unknown client!?",
666: Client_ID(Client));
667: return CONNECTED;
668: }
669:
670: if (target != Client) {
671: Client_Destroy(target, "Got QUIT command",
672: Req->argc == 1 ? quitmsg : NULL, true);
673: return CONNECTED;
674: } else {
675: Conn_Close(Client_Conn(Client), "Got QUIT command",
676: Req->argc == 1 ? quitmsg : NULL, true);
677: return DISCONNECTED;
678: }
679: } else {
680: if (Req->argc == 1 && quitmsg[0] != '\"') {
681: /* " " to avoid confusion */
682: strlcpy(quitmsg, "\"", sizeof quitmsg);
683: strlcat(quitmsg, Req->argv[0], sizeof quitmsg-1);
684: strlcat(quitmsg, "\"", sizeof quitmsg );
685: }
686:
687: /* User, Service, or not yet registered */
688: Conn_Close(Client_Conn(Client), "Got QUIT command",
689: Req->argc == 1 ? quitmsg : NULL, true);
690:
691: return DISCONNECTED;
692: }
693: } /* IRC_QUIT */
694:
695: #ifndef STRICT_RFC
696:
697: /**
698: * Handler for HTTP command, e.g. GET and POST
699: *
700: * We handle these commands here to avoid the quite long timeout when
701: * some user tries to access this IRC daemon using an web browser ...
702: *
703: * @param Client The client from which this command has been received.
704: * @param Req Request structure with prefix and all parameters.
705: * @return CONNECTED or DISCONNECTED.
706: */
707: GLOBAL bool
708: IRC_QUIT_HTTP( CLIENT *Client, REQUEST *Req )
709: {
710: Req->argc = 1;
711: Req->argv[0] = "Oops, HTTP request received? This is IRC!";
712: return IRC_QUIT(Client, Req);
713: } /* IRC_QUIT_HTTP */
714:
715: #endif
716:
717: /**
718: * Handler for the IRC "PING" command.
719: *
720: * @param Client The client from which this command has been received.
721: * @param Req Request structure with prefix and all parameters.
722: * @return CONNECTED or DISCONNECTED.
723: */
724: GLOBAL bool
725: IRC_PING(CLIENT *Client, REQUEST *Req)
726: {
727: CLIENT *target, *from;
728:
729: assert(Client != NULL);
730: assert(Req != NULL);
731:
732: if (Req->argc < 1)
733: return IRC_WriteErrClient(Client, ERR_NOORIGIN_MSG,
734: Client_ID(Client));
735: #ifdef STRICT_RFC
736: /* Don't ignore additional arguments when in "strict" mode */
737: _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
738: #endif
739:
740: if (Req->argc > 1) {
741: /* A target has been specified ... */
742: target = Client_Search(Req->argv[1]);
743:
744: if (!target || Client_Type(target) != CLIENT_SERVER)
745: return IRC_WriteErrClient(Client, ERR_NOSUCHSERVER_MSG,
746: Client_ID(Client), Req->argv[1]);
747:
748: if (target != Client_ThisServer()) {
749: /* Ok, we have to forward the PING */
750: if (Client_Type(Client) == CLIENT_SERVER) {
751: _IRC_REQUIRE_PREFIX_OR_RETURN_(Client, Req)
752: from = Client_Search(Req->prefix);
753: } else
754: from = Client;
755: if (!from)
756: return IRC_WriteErrClient(Client,
757: ERR_NOSUCHSERVER_MSG,
758: Client_ID(Client), Req->prefix);
759:
760: return IRC_WriteStrClientPrefix(target, from,
761: "PING %s :%s", Req->argv[0],
762: Req->argv[1] );
763: }
764: }
765:
766: if (Client_Type(Client) == CLIENT_SERVER) {
767: if (Req->prefix)
768: from = Client_Search(Req->prefix);
769: else
770: from = Client;
771: } else
772: from = Client_ThisServer();
773: if (!from)
774: return IRC_WriteErrClient(Client, ERR_NOSUCHSERVER_MSG,
775: Client_ID(Client), Req->prefix);
776:
777: Log(LOG_DEBUG, "Connection %d: got PING, sending PONG ...",
778: Client_Conn(Client));
779:
780: #ifdef STRICT_RFC
781: return IRC_WriteStrClient(Client, "PONG %s :%s",
782: Client_ID(from), Client_ID(Client));
783: #else
784: /* Some clients depend on the argument being returned in the PONG
785: * reply (not mentioned in any RFC, though) */
786: return IRC_WriteStrClient(Client, "PONG %s :%s",
787: Client_ID(from), Req->argv[0]);
788: #endif
789: } /* IRC_PING */
790:
791: /**
792: * Handler for the IRC "PONG" command.
793: *
794: * @param Client The client from which this command has been received.
795: * @param Req Request structure with prefix and all parameters.
796: * @return CONNECTED or DISCONNECTED.
797: */
798: GLOBAL bool
799: IRC_PONG(CLIENT *Client, REQUEST *Req)
800: {
801: CLIENT *target, *from;
802: CONN_ID conn;
803: #ifndef STRICT_RFC
804: long auth_ping;
805: #endif
806: char *s;
807:
808: assert(Client != NULL);
809: assert(Req != NULL);
810:
811: /* Wrong number of arguments? */
812: if (Req->argc < 1) {
813: if (Client_Type(Client) == CLIENT_USER)
814: return IRC_WriteErrClient(Client, ERR_NOORIGIN_MSG,
815: Client_ID(Client));
816: else
817: return CONNECTED;
818: }
819: if (Client_Type(Client) == CLIENT_USER) {
820: _IRC_ARGC_LE_OR_RETURN_(Client, Req, 2)
821: }
822:
823: /* Forward? */
824: if (Req->argc == 2 && Client_Type(Client) == CLIENT_SERVER) {
825: _IRC_REQUIRE_PREFIX_OR_RETURN_(Client, Req)
826:
827: target = Client_Search(Req->argv[0]);
828: if (!target)
829: return IRC_WriteErrClient(Client, ERR_NOSUCHSERVER_MSG,
830: Client_ID(Client), Req->argv[0]);
831:
832: from = Client_Search(Req->prefix);
833:
834: if (target != Client_ThisServer() && target != from) {
835: /* Ok, we have to forward the message. */
836: if (!from)
837: return IRC_WriteErrClient(Client,
838: ERR_NOSUCHSERVER_MSG,
839: Client_ID(Client), Req->prefix);
840:
841: if (Client_Type(Client_NextHop(target)) != CLIENT_SERVER)
842: s = Client_ID(from);
843: else
844: s = Req->argv[0];
845: return IRC_WriteStrClientPrefix(target, from,
846: "PONG %s :%s", s, Req->argv[1]);
847: }
848: }
849:
850: /* The connection timestamp has already been updated when the data has
851: * been read from so socket, so we don't need to update it here. */
852:
853: conn = Client_Conn(Client);
854:
855: #ifndef STRICT_RFC
856: /* Check authentication PING-PONG ... */
857: auth_ping = Conn_GetAuthPing(conn);
858: if (auth_ping) {
859: LogDebug("AUTH PONG: waiting for token \"%ld\", got \"%s\" ...",
860: auth_ping, Req->argv[0]);
861: if (auth_ping == atol(Req->argv[0])) {
862: Conn_SetAuthPing(conn, 0);
863: if (Client_Type(Client) == CLIENT_WAITAUTHPING)
864: Login_User(Client);
865: } else
866: if (!IRC_WriteStrClient(Client,
867: "NOTICE %s :To connect, type /QUOTE PONG %ld",
868: Client_ID(Client), auth_ping))
869: return DISCONNECTED;
870: }
871: #endif
872:
873: if (Client_Type(Client) == CLIENT_SERVER && Conn_LastPing(conn) == 0) {
874: Log(LOG_INFO,
875: "Synchronization with \"%s\" done (connection %d): %ld second%s [%ld users, %ld channels].",
876: Client_ID(Client), conn,
877: (long)(time(NULL) - Conn_GetSignon(conn)),
878: time(NULL) - Conn_GetSignon(conn) == 1 ? "" : "s",
879: Client_UserCount(), Channel_CountVisible(NULL));
880: }
881: #ifdef DEBUG
882: else {
883: if (Conn_LastPing(conn) > 1)
884: LogDebug("Connection %d: received PONG. Lag: %ld seconds.",
885: conn, (long)(time(NULL) - Conn_LastPing(conn)));
886: else
887: LogDebug("Got unexpected PONG on connection %d. Ignored.",
888: conn);
889: }
890: #endif
891:
892: /* We got a PONG, so signal that none is pending on this connection. */
893: Conn_UpdatePing(conn, 1);
894: return CONNECTED;
895: } /* IRC_PONG */
896:
897: /**
898: * Change the nickname of a client.
899: *
900: * @param Origin The client which caused the nickname change.
901: * @param Target The client of which the nickname should be changed.
902: * @param NewNick The new nickname.
903: */
904: static void
905: Change_Nick(CLIENT *Origin, CLIENT *Target, char *NewNick, bool InformClient)
906: {
907: if (Client_Conn(Target) > NONE) {
908: /* Local client */
909: Log(LOG_INFO,
910: "%s \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".",
911: Client_TypeText(Target), Client_Mask(Target),
912: Client_Conn(Target), Client_ID(Target), NewNick);
913: Conn_UpdateIdle(Client_Conn(Target));
914: } else {
915: /* Remote client */
916: LogDebug("%s \"%s\" changed nick: \"%s\" -> \"%s\".",
917: Client_TypeText(Target),
918: Client_Mask(Target), Client_ID(Target), NewNick);
919: }
920:
921: /* Inform all servers and users (which have to know) of the new name */
922: if (InformClient) {
923: IRC_WriteStrClientPrefix(Target, Target, "NICK :%s", NewNick);
924: IRC_WriteStrServersPrefix(NULL, Target, "NICK :%s", NewNick);
925: } else
926: IRC_WriteStrServersPrefix(Origin, Target, "NICK :%s", NewNick);
927: IRC_WriteStrRelatedPrefix(Target, Target, false, "NICK :%s", NewNick);
928:
929: /* Register old nickname for WHOWAS queries */
930: Client_RegisterWhowas(Target);
931:
932: /* Save new nickname */
933: Client_SetID(Target, NewNick);
934: }
935:
936: /* -eof- */
CVSweb