f5d3e7632ce59247328c7d4857da1cb0073b4833
[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
20 #include "numnicks.h"
21
22 #include "sys.h"
23 #include "h.h"
24 #include "s_serv.h"
25 #include "struct.h"
26 #include "common.h"
27 #include "ircd.h"
28 #include "s_misc.h"
29 #include "match.h"
30 #include "s_bsd.h"
31 #include "s_debug.h"
32
33 #include <assert.h>
34
35 RCSTAG_CC("$Id$");
36
37 /*
38  * Numeric nicks are new as of version ircu2.10.00beta1.
39  *
40  * The idea is as follows:
41  * In most messages (for protocol 10+) the original nick will be
42  * replaced by a 3 character string: YXX
43  * Where 'Y' represents the server, and 'XX' the nick on that server.
44  *
45  * 'YXX' should not interfer with the input parser, and therefore is
46  * not allowed to contain spaces or a ':'.
47  * Also, 'Y' can't start with a '+' because of m_server().
48  *
49  * We keep the characters printable for debugging reasons too.
50  *
51  * The 'XX' value can be larger then the maximum number of clients
52  * per server, we use a mask (struct Server::nn_mask) to get the real
53  * client numeric. The overhead is used to have some redundancy so
54  * just-disconnected-client aren't confused with just-connected ones.
55  */
56
57 /*
58  * when n2k comes, define this for more capacity
59  */
60 #undef  EXTENDED_NUMERICS
61
62 /* These must be the same on ALL servers ! Do not change ! */
63
64 #define NUMNICKLOG 6
65 #define NUMNICKMAXCHAR 'z'      /* See convert2n[] */
66 #define NUMNICKBASE 64          /* (2 << NUMNICKLOG) */
67 #define NUMNICKMASK 63          /* (NUMNICKBASE-1) */
68 #define NN_MAX_SERVER 4096      /* (NUMNICKBASE * NUMNICKBASE) */
69
70 /*
71  * The internal counter for the 'XX' of local clients
72  */
73 static unsigned int lastNNServer = 0;
74 static struct Client *server_list[NN_MAX_SERVER];
75
76 /* *INDENT-OFF* */
77
78 /*
79  * convert2y[] converts a numeric to the corresponding character.
80  * The following characters are currently known to be forbidden:
81  *
82  * '\0' : Because we use '\0' as end of line.
83  *
84  * ' '  : Because parse_*() uses this as parameter seperator.
85  * ':'  : Because parse_server() uses this to detect if a prefix is a
86  *        numeric or a name.
87  * '+'  : Because m_nick() uses this to determine if parv[6] is a
88  *        umode or not.
89  * '&', '#', '+', '$', '@' and '%' :
90  *        Because m_message() matches these characters to detect special cases.
91  */
92 static const char convert2y[] = {
93   'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
94   'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
95   'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
96   'w','x','y','z','0','1','2','3','4','5','6','7','8','9','[',']'
97 };
98
99 static const unsigned int convert2n[] = {
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    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
103   52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0, 
104    0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
105   15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
106    0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
107   41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0,
108
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    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
117 };
118
119 /* *INDENT-ON* */
120
121
122 unsigned int base64toint(const char *s)
123 {
124   unsigned int i = convert2n[(unsigned char)*s++];
125   while (*s)
126   {
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   {
138     buf[--count] = convert2y[(v & NUMNICKMASK)];
139     v >>= NUMNICKLOG;
140   }
141   return buf;
142 }
143
144 static struct Client *FindXNServer(const char *numeric)
145 {
146   char buf[3];
147   buf[0] = *numeric++;
148   buf[1] = *numeric;
149   buf[2] = '\0';
150   Debug((DEBUG_DEBUG, "FindXNServer: %s(%d)", buf, base64toint(buf)));
151   return server_list[base64toint(buf)];
152 }
153
154 struct Client *FindNServer(const char *numeric)
155 {
156   size_t len = strlen(numeric);
157   if (len < 3)
158   {
159     Debug((DEBUG_DEBUG, "FindNServer: %s(%d)", numeric, base64toint(numeric)));
160     return server_list[base64toint(numeric)];
161   }
162   else if (len == 3)
163   {
164     Debug((DEBUG_DEBUG, "FindNServer: %c(%d)", *numeric,
165         convert2n[(unsigned char)*numeric]));
166     return server_list[convert2n[(unsigned char)*numeric]];
167   }
168   return FindXNServer(numeric);
169 }
170
171 void RemoveYXXClient(struct Client *server, const char *yxx)
172 {
173   assert(0 != server);
174   assert(0 != yxx);
175   if (*yxx)
176   {
177     unsigned int index = 0;
178
179     if (strlen(yxx) < 3)
180       index = base64toint(yxx) & server->serv->nn_mask;
181     else
182       index = base64toint(yxx);
183
184     Debug((DEBUG_DEBUG, "RemoveYXXClient: %s(%d)", yxx, index));
185
186     if (index < (server->serv->nn_mask + 1))
187       server->serv->client_list[index] = NULL;
188   }
189 }
190
191 void SetServerYXX(struct Client *cptr, struct Client *server, const char *yxx)
192 {
193   unsigned int index;
194 #ifndef NO_PROTOCOL9
195   /* Use cptr, because we do protocol 9 -> 10 translation for numeric nicks ! */
196   if (Protocol(cptr) > 9)
197   {
198 #endif
199     if (5 == strlen(yxx))
200     {
201       strncpy(server->yxx, yxx, 2);
202       strncpy(server->serv->nn_capacity, yxx + 2, 3);
203     }
204     else
205     {
206       server->yxx[0] = yxx[0];
207       server->serv->nn_capacity[0] = yxx[1];
208       server->serv->nn_capacity[1] = yxx[2];
209     }
210     server->serv->nn_mask = base64toint(server->serv->nn_capacity);
211
212 #ifndef NO_PROTOCOL9
213   }
214   else
215   {
216     static const struct ServerNameNumeric {
217       const char *name;
218       unsigned int numeric;
219     } server_table[] = {
220       { "Uworld.undernet.org",    22},
221       { "Uworld2.undernet.org",   23},
222       { "channels.undernet.org",  30},
223       { "channels2.undernet.org", 31},
224       {
225       0, 0}
226     };
227     int i;
228     for (i = 0; i < 4; ++i)
229     {
230       if (!strCasediff(server_table[i].name, server->name))
231       {
232         /*
233          * XXX - just use the old format for services for now
234          */
235         *server->yxx = convert2y[server_table[i].numeric];
236         inttobase64(server->serv->nn_capacity, 63, 2);
237         server->serv->nn_mask = 63;
238         break;
239       }
240     }
241   }
242 #endif
243   index = base64toint(server->yxx);
244   if (index >= lastNNServer)
245     lastNNServer = index + 1;
246   server_list[index] = server;
247
248   /* Note, exit_one_client uses the fact that `client_list' != NULL to
249    * determine that SetServerYXX has been called - and then calls
250    * ClearServerYXX. However, freeing the allocation happens in free_client() */
251   server->serv->client_list =
252       (struct Client **)RunCalloc(server->serv->nn_mask + 1,
253       sizeof(struct Client *));
254 }
255
256 void SetYXXCapacity(struct Client *c, size_t capacity)
257 {
258   unsigned int max_clients;
259 #if defined(EXTENDED_NUMERICS)
260   max_clients = capacity - 1;
261   inttobase64(c->serv->nn_capacity, max_clients, 3);
262 #else
263   max_clients = 16;
264   /* 
265    * Calculate mask to be used for the maximum number of clients
266    */
267   while (capacity >= max_clients)
268     max_clients = max_clients << 1;
269   /*
270    * Sanity checks
271    */
272   if (max_clients > NN_MAX_SERVER)
273   {
274     fprintf(stderr, "MAXCLIENTS (or MAXCONNECTIONS) is (at least) %d "
275         "too large ! Please decrease this value.\n",
276         max_clients - NN_MAX_SERVER);
277     exit(-1);
278   }
279   --max_clients;
280   inttobase64(c->serv->nn_capacity, max_clients, 2);
281 #endif
282   c->serv->nn_mask = max_clients;       /* Our Numeric Nick mask */
283   c->serv->client_list = (struct Client **)RunCalloc(max_clients + 1,
284       sizeof(struct Client *));
285   server_list[base64toint(c->yxx)] = c;
286 }
287
288 void SetYXXServerName(struct Client *c, unsigned int numeric)
289 {
290   assert(0 != c);
291   assert(numeric < NN_MAX_SERVER);
292
293 #if defined(EXTENDED_NUMERICS)
294   inttobase64(c->yxx, numeric, 2);
295 #else
296   assert(numeric < NUMNICKBASE);
297   c->yxx[0] = convert2y[numeric];
298 #endif
299   if (numeric >= lastNNServer)
300     lastNNServer = numeric + 1;
301   server_list[numeric] = c;
302 }
303
304 void ClearServerYXX(const struct Client *server)
305 {
306   unsigned int index = base64toint(server->yxx);
307   if (server_list[index] == server)     /* Sanity check */
308     server_list[index] = NULL;
309 }
310
311 /*
312  * SetRemoteNumNick()
313  *
314  * Register numeric of new, remote, client.
315  * Add it to the appropriate client_list.
316  */
317 int SetRemoteNumNick(struct Client *acptr, const char *yxx)
318 {
319   struct Client **acptrp;
320   struct Client *server = acptr->user->server;
321   unsigned int index = 0;
322
323   if (5 == strlen(yxx))
324   {
325     strcpy(acptr->yxx, yxx + 2);
326     index = base64toint(acptr->yxx);
327   }
328   else
329   {
330     acptr->yxx[0] = *++yxx;
331     acptr->yxx[1] = *++yxx;
332     acptr->yxx[2] = 0;
333     index = base64toint(acptr->yxx) & server->serv->nn_mask;
334   }
335
336   Debug((DEBUG_DEBUG, "SetRemoteNumNick: %s(%d)", acptr->yxx, index));
337   if (index > server->serv->nn_mask)
338     return 0;
339
340   assert(index <= server->serv->nn_mask);
341
342   acptrp = &server->serv->client_list[index];
343   if (*acptrp)
344   {
345     /*
346      * this exits the old client in the array, not the client
347      * that is being set
348      */
349     exit_client(acptr->from, *acptrp, server, "Numeric nick collision (Ghost)");
350   }
351   *acptrp = acptr;
352   return 1;
353 }
354
355
356 /*
357  * SetLocalNumNick()
358  *
359  * Register numeric of new, local, client. Add it to our client_list.
360  */
361 void SetLocalNumNick(struct Client *cptr)
362 {
363   static unsigned int last_nn = 0;
364   struct Client **client_list = me.serv->client_list;
365   unsigned int capacity = me.serv->nn_mask + 1;
366   unsigned int count = 0;
367
368   assert(cptr->user->server == &me);
369
370   while (client_list[last_nn])
371   {
372     if (++count == capacity)
373     {
374       assert(count < capacity);
375       return;
376     }
377     if (++last_nn == capacity)
378       last_nn = 0;
379   }
380   client_list[last_nn] = cptr;  /* Reserve the numeric ! */
381
382 #if defined(EXTENDED_NUMERICS)
383   inttobase64(cptr->yxx, last_nn, 3);
384 #else
385   inttobase64(cptr->yxx, last_nn, 2);
386 #endif
387 }
388
389 struct Client *findNUser(const char *yxx)
390 {
391   struct Client *server = 0;
392   unsigned int index = 0;
393   if (5 == strlen(yxx))
394   {
395     if (0 != (server = FindXNServer(yxx)))
396     {
397       Debug((DEBUG_DEBUG, "findNUser: %s(%d) (%p)", yxx,
398           base64toint(yxx + 2), server));
399       if ((index = base64toint(yxx + 2)) <= server->serv->nn_mask)
400         return server->serv->client_list[index];
401     }
402   }
403   else if (0 != (server = FindNServer(yxx)))
404   {
405     index = base64toint(yxx + 1) & server->serv->nn_mask;
406     Debug((DEBUG_DEBUG, "findNUser: %s(%d) (%p)", yxx, index, server));
407     return server->serv->client_list[index];
408   }
409   return 0;
410 }
411
412 /* 
413  * markMatchexServer()
414  * Mark all servers whose name matches the given (compiled) mask
415  * and return their count, abusing FLAGS_MAP for this :)
416  */
417 int markMatchexServer(const char *cmask, int minlen)
418 {
419   int cnt = 0;
420   int i;
421   struct Client *acptr;
422
423   for (i = 0; i < lastNNServer; i++)
424   {
425     if ((acptr = server_list[i]))
426     {
427       if (matchexec(acptr->name, cmask, minlen))
428         acptr->flags &= ~FLAGS_MAP;
429       else
430       {
431         acptr->flags |= FLAGS_MAP;
432         cnt++;
433       }
434     }
435   }
436   return cnt;
437 }
438
439 struct Client *find_match_server(char *mask)
440 {
441   struct Client *acptr;
442   int i;
443
444   if (!(BadPtr(mask)))
445   {
446     collapse(mask);
447     for (i = 0; i < lastNNServer; i++)
448     {
449       if ((acptr = server_list[i]) && (!match(mask, acptr->name)))
450         return acptr;
451     }
452   }
453   return NULL;
454 }
455
456
457 #ifndef NO_PROTOCOL9
458
459 /******************************************************************************
460  *
461  * The following functions can be removed as soon as all servers have upgraded
462  * to ircu2.10.
463  */
464
465 /*
466  * CreateNNforProtocol9server
467  *
468  * We do not receive numeric nicks from servers behind a protocol 9 link
469  * so we generate it ourselfs.
470  */
471 const char *CreateNNforProtocol9server(const struct Client *server)
472 {
473   static char YXX[4];
474   struct Server *serv = server->serv;
475   unsigned int count = 0;
476
477   assert(IsServer(server));
478   assert(9 == Protocol(server));
479
480   YXX[0] = *server->yxx;
481   YXX[3] = 0;
482
483   while (serv->client_list[serv->nn_last])
484   {
485     if (++count == NUMNICKBASE)
486     {
487       assert(count < NUMNICKBASE);
488       return NULL;
489     }
490     if (++serv->nn_last == NUMNICKBASE)
491       serv->nn_last = 0;
492   }
493   inttobase64(YXX + 1, serv->nn_last, 2);
494   return YXX;
495 }
496
497 #endif /* !NO_PROTOCOL9 */