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