This commit was generated by cvs2svn to compensate for changes in r2,
[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       {
221       "Uworld.undernet.org", 22},
222       {
223       "Uworld2.undernet.org", 23},
224       {
225       "channels.undernet.org", 30},
226       {
227       "channels2.undernet.org", 31},
228       {
229       0, 0}
230     };
231     int i;
232     for (i = 0; i < 4; ++i)
233     {
234       if (!strCasediff(server_table[i].name, server->name))
235       {
236         /*
237          * XXX - just use the old format for services for now
238          */
239         *server->yxx = convert2y[server_table[i].numeric];
240         inttobase64(server->serv->nn_capacity, 63, 2);
241         server->serv->nn_mask = 63;
242         break;
243       }
244     }
245   }
246 #endif
247   index = base64toint(server->yxx);
248   if (index >= lastNNServer)
249     lastNNServer = index + 1;
250   server_list[index] = server;
251
252   /* Note, exit_one_client uses the fact that `client_list' != NULL to
253    * determine that SetServerYXX has been called - and then calls
254    * ClearServerYXX. However, freeing the allocation happens in free_client() */
255   server->serv->client_list =
256       (struct Client **)RunCalloc(server->serv->nn_mask + 1,
257       sizeof(struct Client *));
258 }
259
260 void SetYXXCapacity(struct Client *c, size_t capacity)
261 {
262   unsigned int max_clients;
263 #if defined(EXTENDED_NUMERICS)
264   max_clients = capacity - 1;
265   inttobase64(c->serv->nn_capacity, max_clients, 3);
266 #else
267   max_clients = 16;
268   /* 
269    * Calculate mask to be used for the maximum number of clients
270    */
271   while (capacity >= max_clients)
272     max_clients = max_clients << 1;
273   /*
274    * Sanity checks
275    */
276   if (max_clients > NN_MAX_SERVER)
277   {
278     fprintf(stderr, "MAXCLIENTS (or MAXCONNECTIONS) is (at least) %d "
279         "too large ! Please decrease this value.\n",
280         max_clients - NN_MAX_SERVER);
281     exit(-1);
282   }
283   --max_clients;
284   inttobase64(c->serv->nn_capacity, max_clients, 2);
285 #endif
286   c->serv->nn_mask = max_clients;       /* Our Numeric Nick mask */
287   c->serv->client_list = (struct Client **)RunCalloc(max_clients + 1,
288       sizeof(struct Client *));
289   server_list[base64toint(c->yxx)] = c;
290 }
291
292 void SetYXXServerName(struct Client *c, unsigned int numeric)
293 {
294   assert(0 != c);
295   assert(numeric < NN_MAX_SERVER);
296
297 #if defined(EXTENDED_NUMERICS)
298   inttobase64(c->yxx, numeric, 2);
299 #else
300   assert(numeric < NUMNICKBASE);
301   c->yxx[0] = convert2y[numeric];
302 #endif
303   if (numeric >= lastNNServer)
304     lastNNServer = numeric + 1;
305   server_list[numeric] = c;
306 }
307
308 void ClearServerYXX(const struct Client *server)
309 {
310   unsigned int index = base64toint(server->yxx);
311   if (server_list[index] == server)     /* Sanity check */
312     server_list[index] = NULL;
313 }
314
315 /*
316  * SetRemoteNumNick()
317  *
318  * Register numeric of new, remote, client.
319  * Add it to the appropriate client_list.
320  */
321 int SetRemoteNumNick(struct Client *acptr, const char *yxx)
322 {
323   struct Client **acptrp;
324   struct Client *server = acptr->user->server;
325   unsigned int index = 0;
326
327   if (5 == strlen(yxx))
328   {
329     strcpy(acptr->yxx, yxx + 2);
330     index = base64toint(acptr->yxx);
331   }
332   else
333   {
334     acptr->yxx[0] = *++yxx;
335     acptr->yxx[1] = *++yxx;
336     acptr->yxx[2] = 0;
337     index = base64toint(acptr->yxx) & server->serv->nn_mask;
338   }
339
340   Debug((DEBUG_DEBUG, "SetRemoteNumNick: %s(%d)", acptr->yxx, index));
341   if (index > server->serv->nn_mask)
342     return 0;
343
344   assert(index <= server->serv->nn_mask);
345
346   acptrp = &server->serv->client_list[index];
347   if (*acptrp)
348   {
349     /*
350      * this exits the old client in the array, not the client
351      * that is being set
352      */
353     exit_client(acptr->from, *acptrp, server, "Numeric nick collision (Ghost)");
354   }
355   *acptrp = acptr;
356   return 1;
357 }
358
359
360 /*
361  * SetLocalNumNick()
362  *
363  * Register numeric of new, local, client. Add it to our client_list.
364  */
365 void SetLocalNumNick(struct Client *cptr)
366 {
367   static unsigned int last_nn = 0;
368   struct Client **client_list = me.serv->client_list;
369   unsigned int capacity = me.serv->nn_mask + 1;
370   unsigned int count = 0;
371
372   assert(cptr->user->server == &me);
373
374   while (client_list[last_nn])
375   {
376     if (++count == capacity)
377     {
378       assert(count < capacity);
379       return;
380     }
381     if (++last_nn == capacity)
382       last_nn = 0;
383   }
384   client_list[last_nn] = cptr;  /* Reserve the numeric ! */
385
386 #if defined(EXTENDED_NUMERICS)
387   inttobase64(cptr->yxx, last_nn, 3);
388 #else
389   inttobase64(cptr->yxx, last_nn, 2);
390 #endif
391 }
392
393 struct Client *findNUser(const char *yxx)
394 {
395   struct Client *server = 0;
396   unsigned int index = 0;
397   if (5 == strlen(yxx))
398   {
399     if (0 != (server = FindXNServer(yxx)))
400     {
401       Debug((DEBUG_DEBUG, "findNUser: %s(%d) (%p)", yxx,
402           base64toint(yxx + 2), server));
403       if ((index = base64toint(yxx + 2)) <= server->serv->nn_mask)
404         return server->serv->client_list[index];
405     }
406   }
407   else if (0 != (server = FindNServer(yxx)))
408   {
409     index = base64toint(yxx + 1) & server->serv->nn_mask;
410     Debug((DEBUG_DEBUG, "findNUser: %s(%d) (%p)", yxx, index, server));
411     return server->serv->client_list[index];
412   }
413   return 0;
414 }
415
416 /* 
417  * markMatchexServer()
418  * Mark all servers whose name matches the given (compiled) mask
419  * and return their count, abusing FLAGS_MAP for this :)
420  */
421 int markMatchexServer(const char *cmask, int minlen)
422 {
423   int cnt = 0;
424   int i;
425   struct Client *acptr;
426
427   for (i = 0; i < lastNNServer; i++)
428   {
429     if ((acptr = server_list[i]))
430     {
431       if (matchexec(acptr->name, cmask, minlen))
432         acptr->flags &= ~FLAGS_MAP;
433       else
434       {
435         acptr->flags |= FLAGS_MAP;
436         cnt++;
437       }
438     }
439   }
440   return cnt;
441 }
442
443 struct Client *find_match_server(char *mask)
444 {
445   struct Client *acptr;
446   int i;
447
448   if (!(BadPtr(mask)))
449   {
450     collapse(mask);
451     for (i = 0; i < lastNNServer; i++)
452     {
453       if ((acptr = server_list[i]) && (!match(mask, acptr->name)))
454         return acptr;
455     }
456   }
457   return NULL;
458 }
459
460
461 #ifndef NO_PROTOCOL9
462
463 /******************************************************************************
464  *
465  * The following functions can be removed as soon as all servers have upgraded
466  * to ircu2.10.
467  */
468
469 /*
470  * CreateNNforProtocol9server
471  *
472  * We do not receive numeric nicks from servers behind a protocol 9 link
473  * so we generate it ourselfs.
474  */
475 const char *CreateNNforProtocol9server(const struct Client *server)
476 {
477   static char YXX[4];
478   struct Server *serv = server->serv;
479   unsigned int count = 0;
480
481   assert(IsServer(server));
482   assert(9 == Protocol(server));
483
484   YXX[0] = *server->yxx;
485   YXX[3] = 0;
486
487   while (serv->client_list[serv->nn_last])
488   {
489     if (++count == NUMNICKBASE)
490     {
491       assert(count < NUMNICKBASE);
492       return NULL;
493     }
494     if (++serv->nn_last == NUMNICKBASE)
495       serv->nn_last = 0;
496   }
497   inttobase64(YXX + 1, serv->nn_last, 2);
498   return YXX;
499 }
500
501 #endif /* !NO_PROTOCOL9 */