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