Annotation of ircnowd/src/ngircd/numeric.c, Revision 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