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