[BACK]Return to numeric.c CVS log [TXT][DIR] Up to [local] / ircnowd / src / ngircd

Annotation of ircnowd/src/ngircd/numeric.c, Revision 1.1.1.1

1.1       tomglok     1: /*
                      2:  * ngIRCd -- The Next Generation IRC Daemon
                      3:  * Copyright (c)2001-2015 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:  * Handlers for IRC numerics sent to the server
                     17:  */
                     18:
                     19: #include <assert.h>
                     20: #include <stdio.h>
                     21: #include <stdlib.h>
                     22: #include <string.h>
                     23: #include <time.h>
                     24:
                     25: #include "conn-func.h"
                     26: #include "conf.h"
                     27: #include "channel.h"
                     28: #include "class.h"
                     29: #include "irc-write.h"
                     30: #include "lists.h"
                     31: #include "log.h"
                     32: #include "parse.h"
                     33:
                     34: #include "numeric.h"
                     35:
                     36: /**
                     37:  * Announce a channel and its users in the network.
                     38:  */
                     39: static bool
                     40: Announce_Channel(CLIENT *Client, CHANNEL *Chan)
                     41: {
                     42:        CL2CHAN *cl2chan;
                     43:        CLIENT *cl;
                     44:        char str[COMMAND_LEN], *ptr;
                     45:        bool njoin, xop;
                     46:
                     47:        /* Check features of remote server */
                     48:        njoin = Conn_Options(Client_Conn(Client)) & CONN_RFC1459 ? false : true;
                     49:        xop = Client_HasFlag(Client, 'X') ? true : false;
                     50:
                     51:        /* Get all the members of this channel */
                     52:        cl2chan = Channel_FirstMember(Chan);
                     53:        snprintf(str, sizeof(str), "NJOIN %s :", Channel_Name(Chan));
                     54:        while (cl2chan) {
                     55:                cl = Channel_GetClient(cl2chan);
                     56:                assert(cl != NULL);
                     57:
                     58:                if (njoin) {
                     59:                        /* RFC 2813: send NJOIN with nicknames and modes
                     60:                         * (if user is channel operator or has voice) */
                     61:                        if (str[strlen(str) - 1] != ':')
                     62:                                strlcat(str, ",", sizeof(str));
                     63:
                     64:                        /* Prepare user prefix (ChanOp, voiced, ...) */
                     65:                        if (xop && Channel_UserHasMode(Chan, cl, 'q'))
                     66:                                strlcat(str, "~", sizeof(str));
                     67:                        if (xop && Channel_UserHasMode(Chan, cl, 'a'))
                     68:                                strlcat(str, "&", sizeof(str));
                     69:                        if (Channel_UserHasMode(Chan, cl, 'o'))
                     70:                                strlcat(str, "@", sizeof(str));
                     71:                        if (xop && Channel_UserHasMode(Chan, cl, 'h'))
                     72:                                strlcat(str, "%", sizeof(str));
                     73:                        if (Channel_UserHasMode(Chan, cl, 'v'))
                     74:                                strlcat(str, "+", sizeof(str));
                     75:
                     76:                        strlcat(str, Client_ID(cl), sizeof(str));
                     77:
                     78:                        /* Send the data if the buffer is "full" */
                     79:                        if (strlen(str) > (sizeof(str) - CLIENT_NICK_LEN - 8)) {
                     80:                                if (!IRC_WriteStrClient(Client, "%s", str))
                     81:                                        return DISCONNECTED;
                     82:                                snprintf(str, sizeof(str), "NJOIN %s :",
                     83:                                         Channel_Name(Chan));
                     84:                        }
                     85:                } else {
                     86:                        /* RFC 1459: no NJOIN, send JOIN and MODE */
                     87:                        if (!IRC_WriteStrClientPrefix(Client, cl, "JOIN %s",
                     88:                                                Channel_Name(Chan)))
                     89:                                return DISCONNECTED;
                     90:                        ptr = Channel_UserModes(Chan, cl);
                     91:                        while (*ptr) {
                     92:                                if (!IRC_WriteStrClientPrefix(Client, cl,
                     93:                                                   "MODE %s +%c %s",
                     94:                                                   Channel_Name(Chan), ptr[0],
                     95:                                                   Client_ID(cl)))
                     96:                                        return DISCONNECTED;
                     97:                                ptr++;
                     98:                        }
                     99:                }
                    100:
                    101:                cl2chan = Channel_NextMember(Chan, cl2chan);
                    102:        }
                    103:
                    104:        /* Data left in the buffer? */
                    105:        if (str[strlen(str) - 1] != ':') {
                    106:                /* Yes, send it ... */
                    107:                if (!IRC_WriteStrClient(Client, "%s", str))
                    108:                        return DISCONNECTED;
                    109:        }
                    110:
                    111:        return CONNECTED;
                    112: } /* Announce_Channel */
                    113:
                    114: /**
                    115:  * Announce new server in the network
                    116:  * @param Client New server
                    117:  * @param Server Existing server in the network
                    118:  */
                    119: static bool
                    120: Announce_Server(CLIENT * Client, CLIENT * Server)
                    121: {
                    122:        CLIENT *c;
                    123:
                    124:        if (Client_Conn(Server) > NONE) {
                    125:                /* Announce the new server to the one already registered
                    126:                 * which is directly connected to the local server */
                    127:                if (!IRC_WriteStrClient
                    128:                    (Server, "SERVER %s %d %d :%s", Client_ID(Client),
                    129:                     Client_Hops(Client) + 1, Client_MyToken(Client),
                    130:                     Client_Info(Client)))
                    131:                        return DISCONNECTED;
                    132:        }
                    133:
                    134:        if (Client_Hops(Server) == 1)
                    135:                c = Client_ThisServer();
                    136:        else
                    137:                c = Client_TopServer(Server);
                    138:
                    139:        /* Inform new server about the one already registered in the network */
                    140:        return IRC_WriteStrClientPrefix(Client, c, "SERVER %s %d %d :%s",
                    141:                Client_ID(Server), Client_Hops(Server) + 1,
                    142:                Client_MyToken(Server), Client_Info(Server));
                    143: } /* Announce_Server */
                    144:
                    145: #ifdef IRCPLUS
                    146:
                    147: /**
                    148:  * Send a specific list to a remote server.
                    149:  */
                    150: static bool
                    151: Send_List(CLIENT *Client, CHANNEL *Chan, struct list_head *Head, char Type)
                    152: {
                    153:        struct list_elem *elem;
                    154:
                    155:        elem = Lists_GetFirst(Head);
                    156:        while (elem) {
                    157:                if (!IRC_WriteStrClient(Client, "MODE %s +%c %s",
                    158:                                        Channel_Name(Chan), Type,
                    159:                                        Lists_GetMask(elem))) {
                    160:                        return DISCONNECTED;
                    161:                }
                    162:                elem = Lists_GetNext(elem);
                    163:        }
                    164:        return CONNECTED;
                    165: }
                    166:
                    167: /**
                    168:  * Synchronize invite, ban, except, and G-Line lists between servers.
                    169:  *
                    170:  * @param Client New server.
                    171:  * @return CONNECTED or DISCONNECTED.
                    172:  */
                    173: static bool
                    174: Synchronize_Lists(CLIENT * Client)
                    175: {
                    176:        CHANNEL *c;
                    177:        struct list_head *head;
                    178:        struct list_elem *elem;
                    179:        time_t t;
                    180:
                    181:        assert(Client != NULL);
                    182:
                    183:        /* g-lines */
                    184:        head = Class_GetList(CLASS_GLINE);
                    185:        elem = Lists_GetFirst(head);
                    186:        while (elem) {
                    187:                t = Lists_GetValidity(elem) - time(NULL);
                    188:                if (!IRC_WriteStrClient(Client, "GLINE %s %ld :%s",
                    189:                                        Lists_GetMask(elem),
                    190:                                        t > 0 ? (long)t : 0,
                    191:                                        Lists_GetReason(elem)))
                    192:                        return DISCONNECTED;
                    193:                elem = Lists_GetNext(elem);
                    194:        }
                    195:
                    196:        c = Channel_First();
                    197:        while (c) {
                    198:                if (!Send_List(Client, c, Channel_GetListExcepts(c), 'e'))
                    199:                        return DISCONNECTED;
                    200:                if (!Send_List(Client, c, Channel_GetListBans(c), 'b'))
                    201:                        return DISCONNECTED;
                    202:                if (!Send_List(Client, c, Channel_GetListInvites(c), 'I'))
                    203:                        return DISCONNECTED;
                    204:                c = Channel_Next(c);
                    205:        }
                    206:        return CONNECTED;
                    207: }
                    208:
                    209: /**
                    210:  * Send CHANINFO commands to a new server (inform it about existing channels).
                    211:  * @param Client New server
                    212:  * @param Chan Channel
                    213:  */
                    214: static bool
                    215: Send_CHANINFO(CLIENT * Client, CHANNEL * Chan)
                    216: {
                    217:        char *modes, *topic, *key;
                    218:        bool has_k, has_l;
                    219:
                    220: #ifdef DEBUG
                    221:        Log(LOG_DEBUG, "Sending CHANINFO commands for \"%s\" ...",
                    222:            Channel_Name(Chan));
                    223: #endif
                    224:
                    225:        modes = Channel_Modes(Chan);
                    226:        topic = Channel_Topic(Chan);
                    227:
                    228:        if (!*modes && !*topic)
                    229:                return CONNECTED;
                    230:
                    231:        has_k = Channel_HasMode(Chan, 'k');
                    232:        has_l = Channel_HasMode(Chan, 'l');
                    233:
                    234:        /* send CHANINFO */
                    235:        if (!has_k && !has_l) {
                    236:                if (!*topic) {
                    237:                        /* "CHANINFO <chan> +<modes>" */
                    238:                        return IRC_WriteStrClient(Client, "CHANINFO %s +%s",
                    239:                                                  Channel_Name(Chan), modes);
                    240:                }
                    241:                /* "CHANINFO <chan> +<modes> :<topic>" */
                    242:                return IRC_WriteStrClient(Client, "CHANINFO %s +%s :%s",
                    243:                                          Channel_Name(Chan), modes, topic);
                    244:        }
                    245:        /* "CHANINFO <chan> +<modes> <key> <limit> :<topic>" */
                    246:        key = Channel_Key(Chan);
                    247:        return IRC_WriteStrClient(Client, "CHANINFO %s +%s %s %lu :%s",
                    248:                                  Channel_Name(Chan), modes,
                    249:                                  has_k ? (key && *key ? key : "*") : "*",
                    250:                                  has_l ? Channel_MaxUsers(Chan) : 0, topic);
                    251: } /* Send_CHANINFO */
                    252:
                    253: #endif /* IRCPLUS */
                    254:
                    255: /**
                    256:  * Handle ENDOFMOTD (376) numeric and login remote server.
                    257:  * The peer is either an IRC server (no IRC+ protocol), or we got the
                    258:  * ENDOFMOTD numeric from an IRC+ server. We have to register the new server.
                    259:  */
                    260: GLOBAL bool
                    261: IRC_Num_ENDOFMOTD(CLIENT * Client, UNUSED REQUEST * Req)
                    262: {
                    263:        int max_hops, i;
                    264:        CLIENT *c;
                    265:        CHANNEL *chan;
                    266:
                    267:        Client_SetType(Client, CLIENT_SERVER);
                    268:
                    269:        Log(LOG_NOTICE | LOG_snotice,
                    270:            "Server \"%s\" registered (connection %d, 1 hop - direct link).",
                    271:            Client_ID(Client), Client_Conn(Client));
                    272:
                    273:        /* Get highest hop count */
                    274:        max_hops = 0;
                    275:        c = Client_First();
                    276:        while (c) {
                    277:                if (Client_Hops(c) > max_hops)
                    278:                        max_hops = Client_Hops(c);
                    279:                c = Client_Next(c);
                    280:        }
                    281:
                    282:        /* Inform the new server about all other servers, and announce the
                    283:         * new server to all the already registered ones. Important: we have
                    284:         * to do this "in order" and can't introduce servers of which the
                    285:         * "toplevel server" isn't known already. */
                    286:        for (i = 0; i < (max_hops + 1); i++) {
                    287:                for (c = Client_First(); c != NULL; c = Client_Next(c)) {
                    288:                        if (Client_Type(c) != CLIENT_SERVER)
                    289:                                continue;       /* not a server */
                    290:                        if (Client_Hops(c) != i)
                    291:                                continue;       /* not actual "nesting level" */
                    292:                        if (c == Client || c == Client_ThisServer())
                    293:                                continue;       /* that's us or the peer! */
                    294:
                    295:                        if (!Announce_Server(Client, c))
                    296:                                return DISCONNECTED;
                    297:                }
                    298:        }
                    299:
                    300:        /* Announce all the users to the new server */
                    301:        c = Client_First();
                    302:        while (c) {
                    303:                if (Client_Type(c) == CLIENT_USER ||
                    304:                    Client_Type(c) == CLIENT_SERVICE) {
                    305:                        if (!Client_Announce(Client, Client_ThisServer(), c))
                    306:                                return DISCONNECTED;
                    307:                }
                    308:                c = Client_Next(c);
                    309:        }
                    310:
                    311:        /* Announce all channels to the new server */
                    312:        chan = Channel_First();
                    313:        while (chan) {
                    314:                if (Channel_IsLocal(chan)) {
                    315:                        chan = Channel_Next(chan);
                    316:                        continue;
                    317:                }
                    318: #ifdef IRCPLUS
                    319:                /* Send CHANINFO if the peer supports it */
                    320:                if (Client_HasFlag(Client, 'C')) {
                    321:                        if (!Send_CHANINFO(Client, chan))
                    322:                                return DISCONNECTED;
                    323:                }
                    324: #endif
                    325:
                    326:                if (!Announce_Channel(Client, chan))
                    327:                        return DISCONNECTED;
                    328:
                    329:                /* Get next channel ... */
                    330:                chan = Channel_Next(chan);
                    331:        }
                    332:
                    333: #ifdef IRCPLUS
                    334:        if (Client_HasFlag(Client, 'L')) {
                    335:                LogDebug("Synchronizing INVITE- and BAN-lists ...");
                    336:                if (!Synchronize_Lists(Client))
                    337:                        return DISCONNECTED;
                    338:        }
                    339: #endif
                    340:
                    341:        if (!IRC_WriteStrClient(Client, "PING :%s",
                    342:            Client_ID(Client_ThisServer())))
                    343:                return DISCONNECTED;
                    344:
                    345:        return CONNECTED;
                    346: } /* IRC_Num_ENDOFMOTD */
                    347:
                    348: /**
                    349:  * Handle ISUPPORT (005) numeric.
                    350:  */
                    351: GLOBAL bool
                    352: IRC_Num_ISUPPORT(CLIENT * Client, REQUEST * Req)
                    353: {
                    354:        int i;
                    355:        char *key, *value;
                    356:
                    357:        for (i = 1; i < Req->argc - 1; i++) {
                    358:                key = Req->argv[i];
                    359:                value = strchr(key, '=');
                    360:                if (value)
                    361:                        *value++ = '\0';
                    362:                else
                    363:                        value = "";
                    364:
                    365:                if (strcmp("NICKLEN", key) == 0) {
                    366:                        if ((unsigned int)atol(value) == Conf_MaxNickLength - 1)
                    367:                                continue;
                    368:
                    369:                        /* Nickname length settings are different! */
                    370:                        Log(LOG_ERR,
                    371:                            "Peer uses incompatible nickname length (%d/%d)! Disconnecting ...",
                    372:                            Conf_MaxNickLength - 1, atoi(value));
                    373:                        Conn_Close(Client_Conn(Client),
                    374:                                   "Incompatible nickname length",
                    375:                                   NULL, false);
                    376:                        return DISCONNECTED;
                    377:                }
                    378:        }
                    379:
                    380:        return CONNECTED;
                    381: } /* IRC_Num_ISUPPORT */
                    382:
                    383: /* -eof- */

CVSweb