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