6bdca9951ff0b31e7a9cb06eecdfb6a94995e460
[ircu2.10.12-pk.git] / ircd / numnicks.c
1 /*
2  * IRC - Internet Relay Chat, ircd/channel.c
3  * Copyright (C) 1996 Carlo Wood (I wish this was C++ - this sucks :/)
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, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * $Id$
20  */
21
22 #include "numnicks.h"
23 #include "client.h"
24 #include "ircd.h"
25 #include "ircd_alloc.h"
26 #include "ircd_string.h"
27 #include "match.h"
28 #include "s_bsd.h"
29 #include "s_debug.h"
30 #include "s_misc.h"
31 #include "struct.h"
32
33 #include <assert.h>
34 #include <stdio.h>
35 #include <string.h>
36
37
38 /*
39  * Numeric nicks are new as of version ircu2.10.00beta1.
40  *
41  * The idea is as follows:
42  * In most messages (for protocol 10+) the original nick will be
43  * replaced by a 3 character string: YXX
44  * Where 'Y' represents the server, and 'XX' the nick on that server.
45  *
46  * 'YXX' should not interfer with the input parser, and therefore is
47  * not allowed to contain spaces or a ':'.
48  * Also, 'Y' can't start with a '+' because of m_server().
49  *
50  * We keep the characters printable for debugging reasons too.
51  *
52  * The 'XX' value can be larger then the maximum number of clients
53  * per server, we use a mask (struct Server::nn_mask) to get the real
54  * client numeric. The overhead is used to have some redundancy so
55  * just-disconnected-client aren't confused with just-connected ones.
56  */
57
58
59 /* These must be the same on ALL servers ! Do not change ! */
60
61 #define NUMNICKLOG 6
62 #define NUMNICKMAXCHAR 'z'      /* See convert2n[] */
63 #define NUMNICKBASE 64          /* (2 << NUMNICKLOG) */
64 #define NUMNICKMASK 63          /* (NUMNICKBASE-1) */
65 #define NN_MAX_SERVER 4096      /* (NUMNICKBASE * NUMNICKBASE) */
66 #define NN_MAX_CLIENT 262144    /* NUMNICKBASE ^ 3 */
67
68 /*
69  * The internal counter for the 'XX' of local clients
70  */
71 static unsigned int lastNNServer = 0;
72 static struct Client* server_list[NN_MAX_SERVER];
73
74 /* *INDENT-OFF* */
75
76 /*
77  * convert2y[] converts a numeric to the corresponding character.
78  * The following characters are currently known to be forbidden:
79  *
80  * '\0' : Because we use '\0' as end of line.
81  *
82  * ' '  : Because parse_*() uses this as parameter seperator.
83  * ':'  : Because parse_server() uses this to detect if a prefix is a
84  *        numeric or a name.
85  * '+'  : Because m_nick() uses this to determine if parv[6] is a
86  *        umode or not.
87  * '&', '#', '+', '$', '@' and '%' :
88  *        Because m_message() matches these characters to detect special cases.
89  */
90 static const char convert2y[] = {
91   'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
92   'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
93   'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
94   'w','x','y','z','0','1','2','3','4','5','6','7','8','9','[',']'
95 };
96
97 static const unsigned int convert2n[] = {
98    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
100    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
101   52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0, 
102    0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
103   15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
104    0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
105   41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0,
106
107    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
108    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
109    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
113    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
114    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
115 };
116
117 /* *INDENT-ON* */
118
119
120 unsigned int base64toint(const char* s)
121 {
122   unsigned int i = convert2n[(unsigned char) *s++];
123   while (*s) {
124     i <<= NUMNICKLOG;
125     i += convert2n[(unsigned char) *s++];
126   }
127   return i;
128 }
129
130 const char* inttobase64(char* buf, unsigned int v, unsigned int count)
131 {
132   buf[count] = '\0';  
133   while (count > 0) {
134     buf[--count] = convert2y[(v & NUMNICKMASK)];
135     v >>= NUMNICKLOG;
136   }
137   return buf;
138 }
139
140 static struct Client* FindXNServer(const char* numeric)
141 {
142   char buf[3];
143   buf[0] = *numeric++;
144   buf[1] = *numeric;
145   buf[2] = '\0';
146   Debug((DEBUG_DEBUG, "FindXNServer: %s(%d)", buf, base64toint(buf)));
147   return server_list[base64toint(buf)];
148 }
149
150 struct Client* FindNServer(const char* numeric)
151 {
152   unsigned int len = strlen(numeric);
153
154   if (len < 3) {
155     Debug((DEBUG_DEBUG, "FindNServer: %s(%d)", numeric, base64toint(numeric)));
156     return server_list[base64toint(numeric)];
157   }
158   else if (len == 3) {
159     Debug((DEBUG_DEBUG, "FindNServer: %c(%d)", *numeric, 
160            convert2n[(unsigned char) *numeric]));
161     return server_list[convert2n[(unsigned char) *numeric]];
162   }
163   return FindXNServer(numeric);
164 }
165
166 struct Client* findNUser(const char* yxx)
167 {
168   struct Client* server = 0;
169   if (5 == strlen(yxx)) {
170     if (0 != (server = FindXNServer(yxx))) {
171       Debug((DEBUG_DEBUG, "findNUser: %s(%d)", yxx, 
172              base64toint(yxx + 2) & cli_serv(server)->nn_mask));
173       return cli_serv(server)->client_list[base64toint(yxx + 2) & cli_serv(server)->nn_mask];
174     }
175   }
176   else if (0 != (server = FindNServer(yxx))) {
177     Debug((DEBUG_DEBUG, "findNUser: %s(%d)",
178            yxx, base64toint(yxx + 1) & cli_serv(server)->nn_mask));
179     return cli_serv(server)->client_list[base64toint(yxx + 1) & cli_serv(server)->nn_mask];
180   }
181   return 0;
182 }
183
184 void RemoveYXXClient(struct Client* server, const char* yxx)
185 {
186   assert(0 != server);
187   assert(0 != yxx);
188   if (*yxx) {
189     Debug((DEBUG_DEBUG, "RemoveYXXClient: %s(%d)", yxx,
190            base64toint(yxx) & cli_serv(server)->nn_mask));
191     cli_serv(server)->client_list[base64toint(yxx) & cli_serv(server)->nn_mask] = 0;
192   }
193 }
194
195 void SetServerYXX(struct Client* cptr, struct Client* server, const char* yxx)
196 {
197   unsigned int index;
198   if (5 == strlen(yxx)) {
199     ircd_strncpy(cli_yxx(server), yxx, 2);
200     ircd_strncpy(cli_serv(server)->nn_capacity, yxx + 2, 3);
201   }
202   else {
203     (cli_yxx(server))[0]               = yxx[0];
204     cli_serv(server)->nn_capacity[0] = yxx[1];
205     cli_serv(server)->nn_capacity[1] = yxx[2];
206   }
207   cli_serv(server)->nn_mask = base64toint(cli_serv(server)->nn_capacity);
208
209   index = base64toint(cli_yxx(server));
210   if (index >= lastNNServer)
211     lastNNServer = index + 1;
212   server_list[index] = server;
213
214   /* Note, exit_one_client uses the fact that `client_list' != NULL to
215    * determine that SetServerYXX has been called - and then calls
216    * ClearServerYXX. However, freeing the allocation happens in free_client() */
217   cli_serv(server)->client_list =
218       (struct Client**) MyCalloc(cli_serv(server)->nn_mask + 1, sizeof(struct Client*));
219 }
220
221 void SetYXXCapacity(struct Client* c, unsigned int capacity)
222 {
223   unsigned int max_clients = 16;
224   /* 
225    * Calculate mask to be used for the maximum number of clients
226    */
227   while (max_clients < capacity)
228     max_clients <<= 1;
229   /*
230    * Sanity checks
231    */
232   if (max_clients > NN_MAX_CLIENT) {
233     fprintf(stderr, "MAXCLIENTS (or MAXCONNECTIONS) is (at least) %d "
234             "too large ! Please decrease this value.\n",
235              max_clients - NN_MAX_CLIENT);
236     exit(-1);
237   }
238   --max_clients;
239   inttobase64(cli_serv(c)->nn_capacity, max_clients, 3); 
240   cli_serv(c)->nn_mask = max_clients;       /* Our Numeric Nick mask */
241   cli_serv(c)->client_list = (struct Client**) MyCalloc(max_clients + 1, 
242                                                      sizeof(struct Client*));
243   server_list[base64toint(cli_yxx(c))] = c;
244 }
245
246 void SetYXXServerName(struct Client* c, unsigned int numeric)
247 {
248   assert(0 != c);
249   assert(numeric < NN_MAX_SERVER);
250
251   inttobase64(cli_yxx(c), numeric, 2);
252   if (numeric >= lastNNServer)
253     lastNNServer = numeric + 1;
254   server_list[numeric] = c;
255 }
256
257 void ClearServerYXX(const struct Client *server)
258 {
259   unsigned int index = base64toint(cli_yxx(server));
260   if (server_list[index] == server)     /* Sanity check */
261     server_list[index] = 0;
262 }
263
264 /*
265  * SetRemoteNumNick()
266  *
267  * Register numeric of new, remote, client.
268  * Add it to the appropriate client_list.
269  */
270 void SetRemoteNumNick(struct Client* acptr, const char *yxx)
271 {
272   struct Client** acptrp;
273   struct Client*  server = cli_user(acptr)->server;
274  
275   if (5 == strlen(yxx)) {
276     strcpy(cli_yxx(acptr), yxx + 2);
277   }
278   else {
279     (cli_yxx(acptr))[0] = *++yxx;
280     (cli_yxx(acptr))[1] = *++yxx;
281     (cli_yxx(acptr))[2] = 0;
282   }
283   Debug((DEBUG_DEBUG, "SetRemoteNumNick: %s(%d)", cli_yxx(acptr), 
284          base64toint(cli_yxx(acptr)) & cli_serv(server)->nn_mask));
285
286   acptrp = &(cli_serv(server))->client_list[base64toint(cli_yxx(acptr)) & cli_serv(server)->nn_mask];
287   if (*acptrp) {
288     /*
289      * this exits the old client in the array, not the client
290      * that is being set
291      */
292     exit_client(cli_from(acptr), *acptrp, server, "Numeric nick collision (Ghost)");
293   }
294   *acptrp = acptr;
295 }
296
297
298 /*
299  * SetLocalNumNick()
300  *
301  * Register numeric of new, local, client. Add it to our client_list.
302  * Muxtex needed if threaded
303  */
304 int SetLocalNumNick(struct Client *cptr)
305 {
306   static unsigned int last_nn     = 0;
307   struct Client**     client_list = cli_serv(&me)->client_list;
308   unsigned int        mask        = cli_serv(&me)->nn_mask;
309   unsigned int        count       = 0;
310
311   assert(cli_user(cptr)->server == &me);
312
313   while (client_list[last_nn & mask]) {
314     if (++count == NN_MAX_CLIENT) {
315       assert(count < NN_MAX_CLIENT);
316       return 0;
317     }
318     if (++last_nn == NN_MAX_CLIENT)
319       last_nn = 0;
320   }
321   client_list[last_nn & mask] = cptr;  /* Reserve the numeric ! */
322
323   inttobase64(cli_yxx(cptr), last_nn, 3);
324   if (++last_nn == NN_MAX_CLIENT)
325     last_nn = 0;
326   return 1;
327 }
328
329 /* 
330  * markMatchexServer()
331  * Mark all servers whose name matches the given (compiled) mask
332  * and return their count, abusing FLAGS_MAP for this :)
333  */
334 int markMatchexServer(const char *cmask, int minlen)
335 {
336   int cnt = 0;
337   int i;
338   struct Client *acptr;
339
340   for (i = 0; i < lastNNServer; i++) {
341     if ((acptr = server_list[i])) {
342       if (matchexec(cli_name(acptr), cmask, minlen))
343         cli_flags(acptr) &= ~FLAGS_MAP;
344       else {
345         cli_flags(acptr) |= FLAGS_MAP;
346         cnt++;
347       }
348     }
349   }
350   return cnt;
351 }
352
353 struct Client* find_match_server(char *mask)
354 {
355   struct Client *acptr;
356   int i;
357
358   if (!(BadPtr(mask))) {
359     collapse(mask);
360     for (i = 0; i < lastNNServer; i++) {
361       if ((acptr = server_list[i]) && (!match(mask, cli_name(acptr))))
362         return acptr;
363     }
364   }
365   return 0;
366 }
367
368