From 024588dbe72e25b49439602670be7d8164183a74 Mon Sep 17 00:00:00 2001 From: Alexander Barton Date: Wed, 21 Nov 2007 12:20:32 +0000 Subject: [PATCH] Funktions to handle numerics sent to the server. --- src/ngircd/numeric.c | 334 +++++++++++++++++++++++++++++++++++++++++++ src/ngircd/numeric.h | 24 ++++ 2 files changed, 358 insertions(+) create mode 100644 src/ngircd/numeric.c create mode 100644 src/ngircd/numeric.h diff --git a/src/ngircd/numeric.c b/src/ngircd/numeric.c new file mode 100644 index 00000000..dd24016b --- /dev/null +++ b/src/ngircd/numeric.c @@ -0,0 +1,334 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * Copyright (c)2001-2007 Alexander Barton (alex@barton.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * Please read the file COPYING, README and AUTHORS for more information. + * + * Handlers for IRC numerics sent to the server + */ + +#include "portab.h" + +static char UNUSED id[] = "$Id: numeric.c,v 1.1 2007/11/21 12:20:32 alex Exp $"; + +#include "imp.h" +#include +#include +#include +#include + +#include "defines.h" +#include "resolve.h" +#include "conn.h" +#include "conf.h" +#include "conn.h" +#include "client.h" +#include "channel.h" +#include "irc-write.h" +#include "lists.h" +#include "log.h" +#include "messages.h" +#include "parse.h" + +#include "exp.h" +#include "numeric.h" + + +/** + * Announce new server in the network + * @param Client New server + * @param Server Existing server in the network + */ +static bool +Announce_Server(CLIENT * Client, CLIENT * Server) +{ + CLIENT *c; + + if (Client_Conn(Server) > NONE) { + /* Announce the new server to the one already registered + * which is directly connected to the local server */ + if (!IRC_WriteStrClient + (Server, "SERVER %s %d %d :%s", Client_ID(Client), + Client_Hops(Client) + 1, Client_MyToken(Client), + Client_Info(Client))) + return DISCONNECTED; + } + + if (Client_Hops(Server) == 1) + c = Client_ThisServer(); + else + c = Client_Introducer(Server); + + /* Inform new server about the one already registered in the network */ + return IRC_WriteStrClientPrefix(Client, c, "SERVER %s %d %d :%s", + Client_ID(Server), Client_Hops(Server) + 1, + Client_MyToken(Server), Client_Info(Server)); +} /* Announce_Server */ + + +/** + * Announce existing user to a new server + * @param Client New server + * @param User Existing user in the network + */ +static bool +Announce_User(CLIENT * Client, CLIENT * User) +{ + return IRC_WriteStrClient(Client, "NICK %s %d %s %s %d +%s :%s", + Client_ID(User), Client_Hops(User) + 1, Client_User(User), + Client_Hostname(User), Client_MyToken(Client_Introducer(User)), + Client_Modes(User), Client_Info(User)); +} /* Announce_User */ + + +#ifdef IRCPLUS + +/** + * Synchronize invite and ban lists between servers + * @param Client New server + */ +static bool +Synchronize_Lists(CLIENT * Client) +{ + CHANNEL *c; + struct list_head *head; + struct list_elem *elem; + + assert(Client != NULL); + + c = Channel_First(); + while (c) { + /* ban list */ + head = Channel_GetListBans(c); + elem = Lists_GetFirst(head); + while (elem) { + if (!IRC_WriteStrClient(Client, "MODE %s +b %s", + Channel_Name(c), + Lists_GetMask(elem))) { + return DISCONNECTED; + } + elem = Lists_GetNext(elem); + } + + /* invite list */ + head = Channel_GetListInvites(c); + elem = Lists_GetFirst(head); + while (elem) { + if (!IRC_WriteStrClient(Client, "MODE %s +I %s", + Channel_Name(c), + Lists_GetMask(elem))) { + return DISCONNECTED; + } + elem = Lists_GetNext(elem); + } + + c = Channel_Next(c); + } + return CONNECTED; +} + + +/** + * Send CHANINFO commands to a new server (inform it about existing channels). + * @param Client New server + * @param Chan Channel + */ +static bool +Send_CHANINFO(CLIENT * Client, CHANNEL * Chan) +{ + char *modes, *topic; + bool has_k, has_l; + +#ifdef DEBUG + Log(LOG_DEBUG, "Sending CHANINFO commands ..."); +#endif + + modes = Channel_Modes(Chan); + topic = Channel_Topic(Chan); + + if (!*modes && !*topic) + return CONNECTED; + + has_k = strchr(modes, 'k') != NULL; + has_l = strchr(modes, 'l') != NULL; + + /* send CHANINFO */ + if (!has_k && !has_l) { + if (!*topic) { + /* "CHANINFO +" */ + return IRC_WriteStrClient(Client, "CHANINFO %s +%s", + Channel_Name(Chan), modes); + } + /* "CHANINFO + :" */ + return IRC_WriteStrClient(Client, "CHANINFO %s +%s :%s", + Channel_Name(Chan), modes, topic); + } + /* "CHANINFO + :" */ + return IRC_WriteStrClient(Client, "CHANINFO %s +%s %s %lu :%s", + Channel_Name(Chan), modes, + has_k ? Channel_Key(Chan) : "*", + has_l ? Channel_MaxUsers(Chan) : 0, topic); +} /* Send_CHANINFO */ + +#endif /* IRCPLUS */ + + +/** + * Handle ENDOFMOTD (376) numeric and login remote server. + * The peer is either an IRC server (no IRC+ protocol), or we got the + * ENDOFMOTD numeric from an IRC+ server. We have to register the new server. + */ +GLOBAL bool +IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req) +{ + char str[LINE_LEN]; + int max_hops, i; + CLIENT *c, *cl; + CHANNEL *chan; + CL2CHAN *cl2chan; + + Client_SetType(Client, CLIENT_SERVER); + + Log(LOG_NOTICE | LOG_snotice, + "Server \"%s\" registered (connection %d, 1 hop - direct link).", + Client_ID(Client), Client_Conn(Client)); + + /* Get highest hop count */ + max_hops = 0; + c = Client_First(); + while (c) { + if (Client_Hops(c) > max_hops) + max_hops = Client_Hops(c); + c = Client_Next(c); + } + + /* Inform the new server about all other servers, and announce the + * new server to all the already registered ones. Important: we have + * to do this "in order" and can't introduce servers of which the + * "toplevel server" isn't known already. */ + for (i = 0; i < (max_hops + 1); i++) { + for (c = Client_First(); c != NULL; c = Client_Next(c)) { + if (Client_Type(c) != CLIENT_SERVER) + continue; /* not a server */ + if (Client_Hops(c) != i) + continue; /* not actual "nesting level" */ + if (c == Client || c == Client_ThisServer()) + continue; /* that's us or the peer! */ + + if (!Announce_Server(Client, c)) + return DISCONNECTED; + } + } + + /* Announce all the users to the new server */ + c = Client_First(); + while (c) { + if (Client_Type(c) == CLIENT_USER) { + if (!Announce_User(Client, c)) + return DISCONNECTED; + } + c = Client_Next(c); + } + + /* Announce all channels to the new server */ + chan = Channel_First(); + while (chan) { +#ifdef IRCPLUS + /* Send CHANINFO if the peer supports it */ + if (strchr(Client_Flags(Client), 'C')) { + if (!Send_CHANINFO(Client, chan)) + return DISCONNECTED; + } +#endif + + /* Get all the members of this channel */ + cl2chan = Channel_FirstMember(chan); + snprintf(str, sizeof(str), "NJOIN %s :", Channel_Name(chan)); + while (cl2chan) { + cl = Channel_GetClient(cl2chan); + assert(cl != NULL); + + /* Nick name, with modes (if applicable) */ + if (str[strlen(str) - 1] != ':') + strlcat(str, ",", sizeof(str)); + if (strchr(Channel_UserModes(chan, cl), 'v')) + strlcat(str, "+", sizeof(str)); + if (strchr(Channel_UserModes(chan, cl), 'o')) + strlcat(str, "@", sizeof(str)); + strlcat(str, Client_ID(cl), sizeof(str)); + + /* Send the data if the buffer is "full" */ + if (strlen(str) > (LINE_LEN - CLIENT_NICK_LEN - 8)) { + if (!IRC_WriteStrClient(Client, "%s", str)) + return DISCONNECTED; + snprintf(str, sizeof(str), "NJOIN %s :", + Channel_Name(chan)); + } + + cl2chan = Channel_NextMember(chan, cl2chan); + } + + /* Data left in the buffer? */ + if (str[strlen(str) - 1] != ':') { + /* Yes, send it ... */ + if (!IRC_WriteStrClient(Client, "%s", str)) + return DISCONNECTED; + } + + /* Get next channel ... */ + chan = Channel_Next(chan); + } + +#ifdef IRCPLUS + if (strchr(Client_Flags(Client), 'L')) { + LogDebug("Synchronizing INVITE- and BAN-lists ..."); + if (!Synchronize_Lists(Client)) + return DISCONNECTED; + } +#endif + + return CONNECTED; +} /* IRC_Num_ENDOFMOTD */ + + +/** + * Handle ISUPPORT (005) numeric. + */ +GLOBAL bool +IRC_Num_ISUPPORT(CLIENT * Client, REQUEST * Req) +{ + int i; + char *key, *value; + + for (i = 1; i < Req->argc - 1; i++) { + key = Req->argv[i]; + value = strchr(key, '='); + if (value) + *value++ = '\0'; + else + value = ""; + + if (strcmp("NICKLEN", key) == 0) { + if ((unsigned int)atol(value) == Conf_MaxNickLength - 1) + continue; + + /* Nick name length settings are different! */ + Log(LOG_ERR, + "Peer uses incompatible nick name length (%d/%d)! Disconnecting ...", + Conf_MaxNickLength - 1, atoi(value)); + Conn_Close(Client_Conn(Client), + "Incompatible nick name length", + NULL, false); + return DISCONNECTED; + } + } + + return CONNECTED; +} /* IRC_Num_ISUPPORT */ + + +/* -eof- */ diff --git a/src/ngircd/numeric.h b/src/ngircd/numeric.h new file mode 100644 index 00000000..eca11bae --- /dev/null +++ b/src/ngircd/numeric.h @@ -0,0 +1,24 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * Copyright (c)2001-2007 by Alexander Barton (alex@barton.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * Please read the file COPYING, README and AUTHORS for more information. + * + * $Id: numeric.h,v 1.1 2007/11/21 12:20:32 alex Exp $ + * + * Handlers for IRC numerics sent to the server (header) + */ + +#ifndef __numeric_h__ +#define __numeric_h__ + +GLOBAL bool IRC_Num_ENDOFMOTD PARAMS((CLIENT *Client, UNUSED REQUEST *Req)); +GLOBAL bool IRC_Num_ISUPPORT PARAMS((CLIENT *Client, REQUEST *Req)); + +#endif + +/* -eof- */ -- 2.39.2