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