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