Author: WT
[ircu2.10.12-pk.git] / ircd / list.c
1 /*
2  * IRC - Internet Relay Chat, ircd/list.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Finland
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 1, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include "sys.h"
22 #include "h.h"
23 #include "struct.h"
24 #include "numeric.h"
25 #include "send.h"
26 #include "s_conf.h"
27 #include "class.h"
28 #include "match.h"
29 #include "ircd.h"
30 #include "s_serv.h"
31 #include "support.h"
32 #include "s_misc.h"
33 #include "s_bsd.h"
34 #include "whowas.h"
35 #include "res.h"
36 #include "common.h"
37 #include "list.h"
38 #include "s_user.h"
39 #include "opercmds.h"
40
41 RCSTAG_CC("$Id$");
42
43 #ifdef DEBUGMODE
44 static struct liststats {
45   int inuse;
46 } cloc, crem, users, servs, links, classs, aconfs;
47 #endif
48
49 void outofmemory();
50
51 #ifdef DEBUGMODE
52 void initlists(void)
53 {
54   memset(&cloc, 0, sizeof(cloc));
55   memset(&crem, 0, sizeof(crem));
56   memset(&users, 0, sizeof(users));
57   memset(&servs, 0, sizeof(servs));
58   memset(&links, 0, sizeof(links));
59   memset(&classs, 0, sizeof(classs));
60   memset(&aconfs, 0, sizeof(aconfs));
61 }
62 #endif
63
64 void outofmemory(void)
65 {
66   Debug((DEBUG_FATAL, "Out of memory: restarting server..."));
67   restart("Out of Memory");
68 }
69
70 /*
71  * Create a new aClient structure and set it to initial state.
72  *
73  *   from == NULL,   create local client (a client connected to a socket).
74  *
75  *   from != NULL,   create remote client (behind a socket associated with
76  *                   the client defined by 'from').
77  *                   ('from' is a local client!!).
78  */
79 aClient *make_client(aClient *from, int status)
80 {
81   Reg1 aClient *cptr = NULL;
82   Reg2 size_t size = CLIENT_REMOTE_SIZE;
83
84   /*
85    * Check freelists first to see if we can grab a client without
86    * having to call malloc.
87    */
88   if (!from)
89     size = CLIENT_LOCAL_SIZE;
90
91   if (!(cptr = (aClient *)RunMalloc(size)))
92     outofmemory();
93   memset(cptr, 0, size);        /* All variables are 0 by default */
94
95 #ifdef  DEBUGMODE
96   if (size == CLIENT_LOCAL_SIZE)
97     cloc.inuse++;
98   else
99     crem.inuse++;
100 #endif
101
102   /* Note: structure is zero (memset) */
103   cptr->from = from ? from : cptr;      /* 'from' of local client is self! */
104   cptr->fd = -1;
105   cptr->status = status;
106   strcpy(cptr->username, "unknown");
107   if (size == CLIENT_LOCAL_SIZE)
108   {
109     cptr->since = cptr->lasttime = cptr->firsttime = now;
110     cptr->lastnick = TStime();
111     cptr->nextnick = now - NICK_DELAY;
112     cptr->nexttarget = now - (TARGET_DELAY * (STARTTARGETS - 1));
113     cptr->authfd = -1;
114   }
115   return (cptr);
116 }
117
118 void free_client(aClient *cptr)
119 {
120   RunFree(cptr);
121 }
122
123 /*
124  * 'make_user' add's an User information block to a client
125  * if it was not previously allocated.
126  */
127 anUser *make_user(aClient *cptr)
128 {
129   Reg1 anUser *user;
130
131   user = cptr->user;
132   if (!user)
133   {
134     if (!(user = (anUser *)RunMalloc(sizeof(anUser))))
135       outofmemory();
136     memset(user, 0, sizeof(anUser));    /* All variables are 0 by default */
137 #ifdef  DEBUGMODE
138     users.inuse++;
139 #endif
140     user->refcnt = 1;
141     *user->host = 0;
142     cptr->user = user;
143   }
144   return user;
145 }
146
147 aServer *make_server(aClient *cptr)
148 {
149   Reg1 aServer *serv = cptr->serv;
150
151   if (!serv)
152   {
153     if (!(serv = (aServer *)RunMalloc(sizeof(aServer))))
154       outofmemory();
155     memset(serv, 0, sizeof(aServer));   /* All variables are 0 by default */
156 #ifdef  DEBUGMODE
157     servs.inuse++;
158 #endif
159     cptr->serv = serv;
160     *serv->by = '\0';
161     DupString(serv->last_error_msg, "<>");      /* String must be non-empty */
162   }
163   return cptr->serv;
164 }
165
166 /*
167  * free_user
168  *
169  * Decrease user reference count by one and realease block, if count reaches 0.
170  */
171 void free_user(anUser *user, aClient *cptr)
172 {
173   if (--user->refcnt == 0)
174   {
175     if (user->away)
176       RunFree(user->away);
177     /*
178      * sanity check
179      */
180     if (user->joined || user->invited || user->channel)
181 #ifdef DEBUGMODE
182       dumpcore("%p user (%s!%s@%s) %p %p %p %d %d",
183           cptr, cptr ? cptr->name : "<noname>",
184           user->username, user->host, user,
185           user->invited, user->channel, user->joined, user->refcnt);
186 #else
187       sendto_ops("* %p user (%s!%s@%s) %p %p %p %d %d *",
188           cptr, cptr ? cptr->name : "<noname>",
189           user->username, user->host, user,
190           user->invited, user->channel, user->joined, user->refcnt);
191 #endif
192     RunFree(user);
193 #ifdef  DEBUGMODE
194     users.inuse--;
195 #endif
196   }
197 }
198
199 /*
200  * Taken the code from ExitOneClient() for this and placed it here.
201  * - avalon
202  */
203 void remove_client_from_list(aClient *cptr)
204 {
205   checklist();
206   if (cptr->prev)
207     cptr->prev->next = cptr->next;
208   else
209   {
210     client = cptr->next;
211     client->prev = NULL;
212   }
213   if (cptr->next)
214     cptr->next->prev = cptr->prev;
215   if (IsUser(cptr) && cptr->user)
216   {
217     add_history(cptr, 0);
218     off_history(cptr);
219   }
220   if (cptr->user)
221     free_user(cptr->user, cptr);
222   if (cptr->serv)
223   {
224     if (cptr->serv->user)
225       free_user(cptr->serv->user, cptr);
226     if (cptr->serv->client_list)
227       RunFree(cptr->serv->client_list);
228     RunFree(cptr->serv->last_error_msg);
229     RunFree(cptr->serv);
230 #ifdef  DEBUGMODE
231     servs.inuse--;
232 #endif
233   }
234 #ifdef  DEBUGMODE
235   if (cptr->fd == -2)
236     cloc.inuse--;
237   else
238     crem.inuse--;
239 #endif
240   free_client(cptr);
241   return;
242 }
243
244 /*
245  * Although only a small routine, it appears in a number of places
246  * as a collection of a few lines...functions like this *should* be
247  * in this file, shouldnt they ?  after all, this is list.c, isn't it ?
248  * -avalon
249  */
250 void add_client_to_list(aClient *cptr)
251 {
252   /*
253    * Since we always insert new clients to the top of the list,
254    * this should mean the "me" is the bottom most item in the list.
255    */
256   cptr->next = client;
257   client = cptr;
258   if (cptr->next)
259     cptr->next->prev = cptr;
260   return;
261 }
262
263 /*
264  * Look for ptr in the linked listed pointed to by link.
265  */
266 Link *find_user_link(Link *lp, aClient *ptr)
267 {
268   if (ptr)
269     while (lp)
270     {
271       if (lp->value.cptr == ptr)
272         return (lp);
273       lp = lp->next;
274     }
275   return NULL;
276 }
277
278 Link *make_link(void)
279 {
280   Reg1 Link *lp;
281
282   lp = (Link *)RunMalloc(sizeof(Link));
283 #ifdef  DEBUGMODE
284   links.inuse++;
285 #endif
286   return lp;
287 }
288
289 void free_link(Link *lp)
290 {
291   RunFree(lp);
292 #ifdef  DEBUGMODE
293   links.inuse--;
294 #endif
295 }
296
297 Dlink *add_dlink(Dlink **lpp, aClient *cp)
298 {
299   register Dlink *lp;
300   lp = (Dlink *)RunMalloc(sizeof(Dlink));
301   lp->value.cptr = cp;
302   lp->prev = NULL;
303   if ((lp->next = *lpp))
304     lp->next->prev = lp;
305   *lpp = lp;
306   return lp;
307 }
308
309 void remove_dlink(Dlink **lpp, Dlink *lp)
310 {
311   if (lp->prev)
312   {
313     if ((lp->prev->next = lp->next))
314       lp->next->prev = lp->prev;
315   }
316   else if ((*lpp = lp->next))
317     lp->next->prev = NULL;
318   RunFree(lp);
319 }
320
321 aConfClass *make_class(void)
322 {
323   Reg1 aConfClass *tmp;
324
325   tmp = (aConfClass *) RunMalloc(sizeof(aConfClass));
326 #ifdef  DEBUGMODE
327   classs.inuse++;
328 #endif
329   return tmp;
330 }
331
332 void free_class(aConfClass * tmp)
333 {
334   RunFree(tmp);
335 #ifdef  DEBUGMODE
336   classs.inuse--;
337 #endif
338 }
339
340 aConfItem *make_conf(void)
341 {
342   Reg1 aConfItem *aconf;
343
344   aconf = (struct ConfItem *)RunMalloc(sizeof(aConfItem));
345 #ifdef  DEBUGMODE
346   aconfs.inuse++;
347 #endif
348   memset(&aconf->ipnum, 0, sizeof(struct in_addr));
349   aconf->next = NULL;
350   aconf->host = aconf->passwd = aconf->name = NULL;
351   aconf->status = CONF_ILLEGAL;
352   aconf->clients = 0;
353   aconf->port = 0;
354   aconf->hold = 0;
355   aconf->confClass = NULL;
356   return (aconf);
357 }
358
359 void delist_conf(aConfItem *aconf)
360 {
361   if (aconf == conf)
362     conf = conf->next;
363   else
364   {
365     aConfItem *bconf;
366
367     for (bconf = conf; aconf != bconf->next; bconf = bconf->next);
368     bconf->next = aconf->next;
369   }
370   aconf->next = NULL;
371 }
372
373 void free_conf(aConfItem *aconf)
374 {
375   del_queries((char *)aconf);
376   RunFree(aconf->host);
377   if (aconf->passwd)
378     memset(aconf->passwd, 0, strlen(aconf->passwd));
379   RunFree(aconf->passwd);
380   RunFree(aconf->name);
381   RunFree(aconf);
382 #ifdef  DEBUGMODE
383   aconfs.inuse--;
384 #endif
385   return;
386 }
387
388 aGline *make_gline(int is_ipmask, char *host, char *reason,
389     char *name, time_t expire)
390 {
391   Reg4 aGline *agline;
392 #ifdef BADCHAN
393   int gtype=0;
394   if(*host == '#' || *host == '&' || *host == '+') 
395     gtype=1; /* BAD CHANNEL GLINE */
396 #endif
397
398   agline = (struct Gline *)RunMalloc(sizeof(aGline));   /* alloc memory */
399   DupString(agline->host, host);        /* copy vital information */
400   DupString(agline->reason, reason);
401   DupString(agline->name, name);
402   agline->expire = expire;
403   agline->gflags = GLINE_ACTIVE;        /* gline is active */
404   if (is_ipmask)
405     SetGlineIsIpMask(agline);
406
407 #ifdef BADCHAN
408   if(gtype)
409   { agline->next = badchan;             /* link it into the list */
410     return (badchan = agline);
411   }
412 #endif
413   agline->next = gline;         /* link it into the list */
414   return (gline = agline);
415 }
416
417 aGline *find_gline(aClient *cptr, aGline **pgline)
418 {
419   Reg3 aGline *agline = gline, *a2gline = NULL;
420
421   while (agline)
422   {                             /* look through all glines */
423     if (agline->expire <= TStime())
424     {                           /* handle expired glines */
425       free_gline(agline, a2gline);
426       agline = a2gline ? a2gline->next : gline;
427       if (!agline)
428         break;                  /* agline == NULL means gline == NULL */
429       continue;
430     }
431
432     /* Does gline match? */
433     /* Added a check against the user's IP address as well -Kev */
434     if ((GlineIsIpMask(agline) ?
435         match(agline->host, inetntoa(cptr->ip)) :
436         match(agline->host, cptr->sockhost)) == 0 &&
437         match(agline->name, cptr->user->username) == 0)
438     {
439       if (pgline)
440         *pgline = a2gline;      /* If they need it, give them the previous gline
441                                    entry (probably for free_gline, below) */
442       return agline;
443     }
444
445     a2gline = agline;
446     agline = agline->next;
447   }
448
449   return NULL;                  /* found no glines */
450 }
451
452 void free_gline(aGline *agline, aGline *pgline)
453 {
454   if (pgline)
455     pgline->next = agline->next;        /* squeeze agline out */
456   else
457   { 
458 #ifdef BADCHAN
459     if(*agline->host =='#' || *agline->host == '&' || *agline->host == '+')
460     {
461       badchan = agline->next;
462     }
463     else
464 #endif
465       gline = agline->next;
466   }
467
468   RunFree(agline->host);        /* and free up the memory */
469   RunFree(agline->reason);
470   RunFree(agline->name);
471   RunFree(agline);
472 }
473
474 #ifdef BADCHAN
475 int bad_channel(char *name)
476 { aGline *agline;
477
478   agline=badchan;
479   while(agline)
480   { 
481     if ((agline->gflags&GLINE_ACTIVE) && (agline->expire >TStime()) && 
482          !mmatch(agline->host,name))
483     { return 1;
484     }
485     agline=agline->next;
486   }
487   return 0;
488 }
489 #endif
490
491 #ifdef  DEBUGMODE
492 void send_listinfo(aClient *cptr, char *name)
493 {
494   int inuse = 0, mem = 0, tmp = 0;
495
496   sendto_one(cptr, ":%s %d %s :Local: inuse: %d(%d)",
497       me.name, RPL_STATSDEBUG, name, inuse += cloc.inuse,
498       tmp = cloc.inuse * CLIENT_LOCAL_SIZE);
499   mem += tmp;
500   sendto_one(cptr, ":%s %d %s :Remote: inuse: %d(%d)",
501       me.name, RPL_STATSDEBUG, name,
502       crem.inuse, tmp = crem.inuse * CLIENT_REMOTE_SIZE);
503   mem += tmp;
504   inuse += crem.inuse;
505   sendto_one(cptr, ":%s %d %s :Users: inuse: %d(%d)",
506       me.name, RPL_STATSDEBUG, name, users.inuse,
507       tmp = users.inuse * sizeof(anUser));
508   mem += tmp;
509   inuse += users.inuse,
510       sendto_one(cptr, ":%s %d %s :Servs: inuse: %d(%d)",
511       me.name, RPL_STATSDEBUG, name, servs.inuse,
512       tmp = servs.inuse * sizeof(aServer));
513   mem += tmp;
514   inuse += servs.inuse,
515       sendto_one(cptr, ":%s %d %s :Links: inuse: %d(%d)",
516       me.name, RPL_STATSDEBUG, name, links.inuse,
517       tmp = links.inuse * sizeof(Link));
518   mem += tmp;
519   inuse += links.inuse,
520       sendto_one(cptr, ":%s %d %s :Classes: inuse: %d(%d)",
521       me.name, RPL_STATSDEBUG, name, classs.inuse,
522       tmp = classs.inuse * sizeof(aConfClass));
523   mem += tmp;
524   inuse += classs.inuse,
525       sendto_one(cptr, ":%s %d %s :Confs: inuse: %d(%d)",
526       me.name, RPL_STATSDEBUG, name, aconfs.inuse,
527       tmp = aconfs.inuse * sizeof(aConfItem));
528   mem += tmp;
529   inuse += aconfs.inuse,
530       sendto_one(cptr, ":%s %d %s :Totals: inuse %d %d",
531       me.name, RPL_STATSDEBUG, name, inuse, mem);
532 }
533
534 #endif