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   inttobase64(c->serv->nn_capacity, max_clients, 2); 
248   c->serv->nn_mask = max_clients;       /* Our Numeric Nick mask */
249   c->serv->client_list = (struct Client**) MyCalloc(max_clients + 1, 
250                                                      sizeof(struct Client*));
251   server_list[base64toint(c->yxx)] = c;
252 }
253
254 void SetYXXServerName(struct Client* c, unsigned int numeric)
255 {
256   assert(0 != c);
257   assert(numeric < NN_MAX_SERVER);
258
259 #if defined(EXTENDED_NUMERICS)
260   inttobase64(c->yxx, numeric, 2);
261 #else
262   assert(numeric < NUMNICKBASE);
263   c->yxx[0] = convert2y[numeric];
264 #endif
265   if (numeric >= lastNNServer)
266     lastNNServer = numeric + 1;
267   server_list[numeric] = c;
268 }
269
270 void ClearServerYXX(const struct Client *server)
271 {
272   unsigned int index = base64toint(server->yxx);
273   if (server_list[index] == server)     /* Sanity check */
274     server_list[index] = 0;
275 }
276
277 /*
278  * SetRemoteNumNick()
279  *
280  * Register numeric of new, remote, client.
281  * Add it to the appropriate client_list.
282  */
283 void SetRemoteNumNick(struct Client* acptr, const char *yxx)
284 {
285   struct Client** acptrp;
286   struct Client*  server = acptr->user->server;
287  
288   if (5 == strlen(yxx)) {
289     strcpy(acptr->yxx, yxx + 2);
290   }
291   else {
292     acptr->yxx[0] = *++yxx;
293     acptr->yxx[1] = *++yxx;
294     acptr->yxx[2] = 0;
295   }
296   Debug((DEBUG_DEBUG, "SetRemoteNumNick: %s(%d)", acptr->yxx, 
297          base64toint(acptr->yxx) & server->serv->nn_mask));
298
299   acptrp = &server->serv->client_list[base64toint(acptr->yxx) & server->serv->nn_mask];
300   if (*acptrp) {
301     /*
302      * this exits the old client in the array, not the client
303      * that is being set
304      */
305     exit_client(acptr->from, *acptrp, server, "Numeric nick collision (Ghost)");
306   }
307   *acptrp = acptr;
308 }
309
310
311 /*
312  * SetLocalNumNick()
313  *
314  * Register numeric of new, local, client. Add it to our client_list.
315  * Muxtex needed if threaded
316  */
317 int SetLocalNumNick(struct Client *cptr)
318 {
319   static unsigned int last_nn     = 0;
320   struct Client**     client_list = me.serv->client_list;
321   unsigned int        mask        = me.serv->nn_mask;
322   unsigned int        count       = 0;
323
324   assert(cptr->user->server == &me);
325
326   while (client_list[last_nn & mask]) {
327     if (++count == NN_MAX_CLIENT) {
328       assert(count < NN_MAX_CLIENT);
329       return 0;
330     }
331     if (++last_nn == NN_MAX_CLIENT)
332       last_nn = 0;
333   }
334   client_list[last_nn & mask] = cptr;  /* Reserve the numeric ! */
335
336 #if defined(EXTENDED_NUMERICS)
337   inttobase64(cptr->yxx, last_nn, 3);
338 #else
339   inttobase64(cptr->yxx, last_nn, 2);
340 #endif
341   if (++last_nn == NN_MAX_CLIENT)
342     last_nn = 0;
343   return 1;
344 }
345
346 /* 
347  * markMatchexServer()
348  * Mark all servers whose name matches the given (compiled) mask
349  * and return their count, abusing FLAGS_MAP for this :)
350  */
351 int markMatchexServer(const char *cmask, int minlen)
352 {
353   int cnt = 0;
354   int i;
355   struct Client *acptr;
356
357   for (i = 0; i < lastNNServer; i++) {
358     if ((acptr = server_list[i])) {
359       if (matchexec(acptr->name, cmask, minlen))
360         acptr->flags &= ~FLAGS_MAP;
361       else {
362         acptr->flags |= FLAGS_MAP;
363         cnt++;
364       }
365     }
366   }
367   return cnt;
368 }
369
370 struct Client* find_match_server(char *mask)
371 {
372   struct Client *acptr;
373   int i;
374
375   if (!(BadPtr(mask))) {
376     collapse(mask);
377     for (i = 0; i < lastNNServer; i++) {
378       if ((acptr = server_list[i]) && (!match(mask, acptr->name)))
379         return acptr;
380     }
381   }
382   return 0;
383 }
384
385