Author: Bleep <tomh@inxpress.net>
[ircu2.10.12-pk.git] / ircd / m_trace.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_trace.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * See file AUTHORS in IRC package for additional names of
7  * the programmers.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 1, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * $Id$
24  */
25
26 /*
27  * m_functions execute protocol messages on this server:
28  *
29  *    cptr    is always NON-NULL, pointing to a *LOCAL* client
30  *            structure (with an open socket connected!). This
31  *            identifies the physical socket where the message
32  *            originated (or which caused the m_function to be
33  *            executed--some m_functions may call others...).
34  *
35  *    sptr    is the source of the message, defined by the
36  *            prefix part of the message if present. If not
37  *            or prefix not found, then sptr==cptr.
38  *
39  *            (!IsServer(cptr)) => (cptr == sptr), because
40  *            prefixes are taken *only* from servers...
41  *
42  *            (IsServer(cptr))
43  *                    (sptr == cptr) => the message didn't
44  *                    have the prefix.
45  *
46  *                    (sptr != cptr && IsServer(sptr) means
47  *                    the prefix specified servername. (?)
48  *
49  *                    (sptr != cptr && !IsServer(sptr) means
50  *                    that message originated from a remote
51  *                    user (not local).
52  *
53  *            combining
54  *
55  *            (!IsServer(sptr)) means that, sptr can safely
56  *            taken as defining the target structure of the
57  *            message in this server.
58  *
59  *    *Always* true (if 'parse' and others are working correct):
60  *
61  *    1)      sptr->from == cptr  (note: cptr->from == cptr)
62  *
63  *    2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64  *            *cannot* be a local connection, unless it's
65  *            actually cptr!). [MyConnect(x) should probably
66  *            be defined as (x == x->from) --msa ]
67  *
68  *    parc    number of variable parameter strings (if zero,
69  *            parv is allowed to be NULL)
70  *
71  *    parv    a NULL terminated list of parameter pointers,
72  *
73  *                    parv[0], sender (prefix string), if not present
74  *                            this points to an empty string.
75  *                    parv[1]...parv[parc-1]
76  *                            pointers to additional parameters
77  *                    parv[parc] == NULL, *always*
78  *
79  *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
80  *                    non-NULL pointers.
81  */
82 #if 0
83 /*
84  * No need to include handlers.h here the signatures must match
85  * and we don't need to force a rebuild of all the handlers everytime
86  * we add a new one to the list. --Bleep
87  */
88 #include "handlers.h"
89 #endif /* 0 */
90 #include "class.h"
91 #include "client.h"
92 #include "hash.h"
93 #include "ircd.h"
94 #include "ircd_reply.h"
95 #include "ircd_string.h"
96 #include "match.h"
97 #include "msg.h"
98 #include "numeric.h"
99 #include "numnicks.h"
100 #include "s_bsd.h"
101 #include "s_conf.h"
102 #include "s_user.h"
103 #include "send.h"
104 #include "version.h"
105
106 #include <assert.h>
107 #include <string.h>
108
109 /*
110  * m_trace - generic message handler
111  *
112  * parv[0] = sender prefix
113  * parv[1] = nick or servername
114  * parv[2] = 'target' servername
115  */
116 int m_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
117 {
118   int i;
119   struct Client *acptr;
120   struct ConfClass *cltmp;
121   char *tname;
122   int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
123   int cnt = 0, wilds, dow;
124
125   if (parc < 2 || BadPtr(parv[1]))
126   {
127     /* just "TRACE" without parameters. Must be from local client */
128     parc = 1;
129     acptr = &me;
130     tname = me.name;
131     i = HUNTED_ISME;
132   }
133   else if (parc < 3 || BadPtr(parv[2]))
134   {
135     /* No target specified. Make one before propagating. */
136     parc = 2;
137     tname = parv[1];
138     if ((acptr = find_match_server(parv[1])) ||
139         ((acptr = FindClient(parv[1])) && !MyUser(acptr)))
140     {
141       if (IsUser(acptr))
142         parv[2] = acptr->user->server->name;
143       else
144         parv[2] = acptr->name;
145       parc = 3;
146       parv[3] = 0;
147       if ((i = hunt_server(IsServer(acptr), cptr, sptr,
148           "%s%s " TOK_TRACE " %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
149         return 0;
150     }
151     else
152       i = HUNTED_ISME;
153   }
154   else
155   {
156     /* Got "TRACE <tname> :<target>" */
157     parc = 3;
158     if (MyUser(sptr) || Protocol(cptr) < 10)
159       acptr = find_match_server(parv[2]);
160     else
161       acptr = FindNServer(parv[2]);
162     if ((i = hunt_server(0, cptr, sptr,
163         "%s%s " TOK_TRACE " %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
164       return 0;
165     tname = parv[1];
166   }
167
168   if (i == HUNTED_PASS)
169   {
170     if (!acptr)
171       acptr = next_client(GlobalClientList, tname);
172     else
173       acptr = acptr->from;
174     sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0],
175 #ifndef GODMODE
176         version, debugmode, tname, acptr ? acptr->from->name : "<No_match>");
177 #else /* GODMODE */
178         version, debugmode, tname, acptr ? acptr->from->name : "<No_match>",
179         (acptr && acptr->from->serv) ? acptr->from->serv->timestamp : 0);
180 #endif /* GODMODE */
181     return 0;
182   }
183
184   doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : 1;
185   wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
186   dow = wilds || doall;
187
188   /* Don't give (long) remote listings to lusers */
189   if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
190     return 0;
191
192   for (i = 0; i < MAXCONNECTIONS; i++)
193     link_s[i] = 0, link_u[i] = 0;
194
195   if (doall)
196   {
197     for (acptr = GlobalClientList; acptr; acptr = acptr->next) {
198       if (IsUser(acptr))
199         link_u[acptr->from->fd]++;
200       else if (IsServer(acptr))
201         link_s[acptr->from->fd]++;
202     }
203   }
204
205   /* report all direct connections */
206
207   for (i = 0; i <= HighestFd; i++)
208   {
209     unsigned int conClass;
210
211     if (!(acptr = LocalClientArray[i])) /* Local Connection? */
212       continue;
213     if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
214         !IsAnOper(acptr) && (acptr != sptr))
215       continue;
216     if (!doall && wilds && match(tname, acptr->name))
217       continue;
218     if (!dow && 0 != ircd_strcmp(tname, acptr->name))
219       continue;
220
221     conClass = get_client_class(acptr);
222
223     switch (acptr->status)
224     {
225       case STAT_CONNECTING:
226         sendto_one(sptr, rpl_str(RPL_TRACECONNECTING),
227                    me.name, parv[0], conClass, acptr->name);
228         cnt++;
229         break;
230       case STAT_HANDSHAKE:
231         sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE),
232                    me.name, parv[0], conClass, acptr->name);
233         cnt++;
234         break;
235       case STAT_ME:
236         break;
237       case STAT_UNKNOWN:
238       case STAT_UNKNOWN_USER:
239         sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
240             me.name, parv[0], conClass, get_client_name(acptr, HIDE_IP));
241         cnt++;
242         break;
243       case STAT_UNKNOWN_SERVER:
244         sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
245                    me.name, parv[0], conClass, "Unknown Server");
246         cnt++;
247         break;
248       case STAT_USER:
249         /* Only opers see users if there is a wildcard
250            but anyone can see all the opers. */
251         if ((IsAnOper(sptr) && (MyUser(sptr) ||
252             !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr))
253         {
254           if (IsAnOper(acptr))
255             sendto_one(sptr, rpl_str(RPL_TRACEOPERATOR),
256                 me.name, parv[0], conClass, get_client_name(acptr, HIDE_IP),
257                 CurrentTime - acptr->lasttime);
258           else
259             sendto_one(sptr, rpl_str(RPL_TRACEUSER),
260                 me.name, parv[0], conClass, get_client_name(acptr, HIDE_IP),
261                 CurrentTime - acptr->lasttime);
262           cnt++;
263         }
264         break;
265         /*
266          * Connection is a server
267          *
268          * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
269          *
270          * class        Class the server is in
271          * nS           Number of servers reached via this link
272          * nC           Number of clients reached via this link
273          * name         Name of the server linked
274          * ConnBy       Who established this link
275          * last         Seconds since we got something from this link
276          * age          Seconds this link has been alive
277          *
278          * Additional comments etc......        -Cym-<cym@acrux.net>
279          */
280
281       case STAT_SERVER:
282         if (acptr->serv->user)
283           sendto_one(sptr, rpl_str(RPL_TRACESERVER),
284                      me.name, parv[0], conClass, link_s[i],
285                      link_u[i], acptr->name,
286                      (*acptr->serv->by) ? acptr->serv->by : "*",
287                      acptr->serv->user->username, acptr->serv->user->host,
288                      CurrentTime - acptr->lasttime,
289                      CurrentTime - acptr->serv->timestamp);
290         else
291           sendto_one(sptr, rpl_str(RPL_TRACESERVER),
292                      me.name, parv[0], conClass, link_s[i],
293                      link_u[i], acptr->name,
294                      (*acptr->serv->by) ?  acptr->serv->by : "*", "*",
295                      me.name, CurrentTime - acptr->lasttime,
296                      CurrentTime - acptr->serv->timestamp);
297         cnt++;
298         break;
299       default:                  /* We actually shouldn't come here, -msa */
300         sendto_one(sptr, rpl_str(RPL_TRACENEWTYPE), me.name, parv[0],
301                    get_client_name(acptr, HIDE_IP));
302         cnt++;
303         break;
304     }
305   }
306   /*
307    * Add these lines to summarize the above which can get rather long
308    * and messy when done remotely - Avalon
309    */
310   if (!IsAnOper(sptr) || !cnt)
311   {
312     if (!cnt)
313       /* let the user have some idea that its at the end of the trace */
314       sendto_one(sptr, rpl_str(RPL_TRACESERVER),
315                  me.name, parv[0], 0, link_s[me.fd],
316                  link_u[me.fd], "<No_match>", *(me.serv->by) ?
317                  me.serv->by : "*", "*", me.name, 0, 0);
318     return 0;
319   }
320   for (cltmp = FirstClass(); doall && cltmp; cltmp = NextClass(cltmp))
321     if (Links(cltmp) > 0)
322       sendto_one(sptr, rpl_str(RPL_TRACECLASS), me.name,
323                  parv[0], ConClass(cltmp), Links(cltmp));
324   return 0;
325 }
326
327 /*
328  * ms_trace - server message handler
329  *
330  * parv[0] = sender prefix
331  * parv[1] = nick or servername
332  * parv[2] = 'target' servername
333  */
334 int ms_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
335 {
336   int i;
337   struct Client *acptr;
338   struct ConfClass *cltmp;
339   char *tname;
340   int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
341   int cnt = 0, wilds, dow;
342
343   if (parc < 2 || BadPtr(parv[1]))
344   {
345     /* just "TRACE" without parameters. Must be from local client */
346     parc = 1;
347     acptr = &me;
348     tname = me.name;
349     i = HUNTED_ISME;
350   }
351   else if (parc < 3 || BadPtr(parv[2]))
352   {
353     /* No target specified. Make one before propagating. */
354     parc = 2;
355     tname = parv[1];
356     if ((acptr = find_match_server(parv[1])) ||
357         ((acptr = FindClient(parv[1])) && !MyUser(acptr)))
358     {
359       if (IsUser(acptr))
360         parv[2] = acptr->user->server->name;
361       else
362         parv[2] = acptr->name;
363       parc = 3;
364       parv[3] = 0;
365       if ((i = hunt_server(IsServer(acptr), cptr, sptr,
366           "%s%s " TOK_TRACE " %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
367         return 0;
368     }
369     else
370       i = HUNTED_ISME;
371   }
372   else
373   {
374     /* Got "TRACE <tname> :<target>" */
375     parc = 3;
376     if (MyUser(sptr) || Protocol(cptr) < 10)
377       acptr = find_match_server(parv[2]);
378     else
379       acptr = FindNServer(parv[2]);
380     if ((i = hunt_server(0, cptr, sptr,
381         "%s%s " TOK_TRACE " %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
382       return 0;
383     tname = parv[1];
384   }
385
386   if (i == HUNTED_PASS)
387   {
388     if (!acptr)
389       acptr = next_client(GlobalClientList, tname);
390     else
391       acptr = acptr->from;
392     sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0],
393 #ifndef GODMODE
394         version, debugmode, tname, acptr ? acptr->from->name : "<No_match>");
395 #else /* GODMODE */
396         version, debugmode, tname, acptr ? acptr->from->name : "<No_match>",
397         (acptr && acptr->from->serv) ? acptr->from->serv->timestamp : 0);
398 #endif /* GODMODE */
399     return 0;
400   }
401
402   doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : 1;
403   wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
404   dow = wilds || doall;
405
406   /* Don't give (long) remote listings to lusers */
407   if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
408     return 0;
409
410   for (i = 0; i < MAXCONNECTIONS; i++)
411     link_s[i] = 0, link_u[i] = 0;
412
413   if (doall)
414   {
415     for (acptr = GlobalClientList; acptr; acptr = acptr->next) {
416       if (IsUser(acptr))
417         link_u[acptr->from->fd]++;
418       else if (IsServer(acptr))
419         link_s[acptr->from->fd]++;
420     }
421   }
422
423   /* report all direct connections */
424
425   for (i = 0; i <= HighestFd; i++)
426   {
427     unsigned int conClass;
428
429     if (!(acptr = LocalClientArray[i])) /* Local Connection? */
430       continue;
431     if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
432         !IsAnOper(acptr) && (acptr != sptr))
433       continue;
434     if (!doall && wilds && match(tname, acptr->name))
435       continue;
436     if (!dow && 0 != ircd_strcmp(tname, acptr->name))
437       continue;
438     conClass = get_client_class(acptr);
439
440     switch (acptr->status)
441     {
442       case STAT_CONNECTING:
443         sendto_one(sptr, rpl_str(RPL_TRACECONNECTING),
444                    me.name, parv[0], conClass, acptr->name);
445         cnt++;
446         break;
447       case STAT_HANDSHAKE:
448         sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE),
449                    me.name, parv[0], conClass, acptr->name);
450         cnt++;
451         break;
452       case STAT_ME:
453         break;
454       case STAT_UNKNOWN:
455       case STAT_UNKNOWN_USER:
456         sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
457                    me.name, parv[0], conClass, get_client_name(acptr, HIDE_IP));
458         cnt++;
459         break;
460       case STAT_UNKNOWN_SERVER:
461         sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
462                    me.name, parv[0], conClass, "Unknown Server");
463         cnt++;
464         break;
465       case STAT_USER:
466         /* Only opers see users if there is a wildcard
467            but anyone can see all the opers. */
468         if ((IsAnOper(sptr) && (MyUser(sptr) ||
469             !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr))
470         {
471           if (IsAnOper(acptr))
472             sendto_one(sptr, rpl_str(RPL_TRACEOPERATOR),
473                 me.name, parv[0], conClass, get_client_name(acptr, HIDE_IP),
474                 CurrentTime - acptr->lasttime);
475           else
476             sendto_one(sptr, rpl_str(RPL_TRACEUSER),
477                 me.name, parv[0], conClass, get_client_name(acptr, HIDE_IP),
478                 CurrentTime - acptr->lasttime);
479           cnt++;
480         }
481         break;
482         /*
483          * Connection is a server
484          *
485          * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
486          *
487          * class        Class the server is in
488          * nS           Number of servers reached via this link
489          * nC           Number of clients reached via this link
490          * name         Name of the server linked
491          * ConnBy       Who established this link
492          * last         Seconds since we got something from this link
493          * age          Seconds this link has been alive
494          *
495          * Additional comments etc......        -Cym-<cym@acrux.net>
496          */
497
498       case STAT_SERVER:
499         if (acptr->serv->user)
500           sendto_one(sptr, rpl_str(RPL_TRACESERVER),
501                      me.name, parv[0], conClass, link_s[i],
502                      link_u[i], acptr->name,
503                      (*acptr->serv->by) ? acptr->serv->by : "*",
504                      acptr->serv->user->username, acptr->serv->user->host,
505                      CurrentTime - acptr->lasttime,
506                      CurrentTime - acptr->serv->timestamp);
507         else
508           sendto_one(sptr, rpl_str(RPL_TRACESERVER),
509                      me.name, parv[0], conClass, link_s[i],
510                      link_u[i], acptr->name,
511                      (*acptr->serv->by) ?  acptr->serv->by : "*", "*",
512                      me.name, CurrentTime - acptr->lasttime,
513                      CurrentTime - acptr->serv->timestamp);
514         cnt++;
515         break;
516       default:                  /* We actually shouldn't come here, -msa */
517         sendto_one(sptr, rpl_str(RPL_TRACENEWTYPE), me.name, parv[0],
518                    get_client_name(acptr, HIDE_IP));
519         cnt++;
520         break;
521     }
522   }
523   /*
524    * Add these lines to summarize the above which can get rather long
525    * and messy when done remotely - Avalon
526    */
527   if (!IsAnOper(sptr) || !cnt)
528   {
529     if (!cnt)
530       /* let the user have some idea that its at the end of the trace */
531       sendto_one(sptr, rpl_str(RPL_TRACESERVER),
532           me.name, parv[0], 0, link_s[me.fd],
533           link_u[me.fd], "<No_match>", *(me.serv->by) ?
534           me.serv->by : "*", "*", me.name, 0, 0);
535     return 0;
536   }
537   for (cltmp = FirstClass(); doall && cltmp; cltmp = NextClass(cltmp))
538     if (Links(cltmp) > 0)
539       sendto_one(sptr, rpl_str(RPL_TRACECLASS), me.name,
540           parv[0], ConClass(cltmp), Links(cltmp));
541   return 0;
542 }
543
544 /*
545  * mo_trace - oper message handler
546  *
547  * parv[0] = sender prefix
548  * parv[1] = nick or servername
549  * parv[2] = 'target' servername
550  */
551 int mo_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
552 {
553   int i;
554   struct Client *acptr;
555   struct ConfClass *cltmp;
556   char *tname;
557   int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
558   int cnt = 0, wilds, dow;
559
560   if (parc < 2 || BadPtr(parv[1]))
561   {
562     /* just "TRACE" without parameters. Must be from local client */
563     parc = 1;
564     acptr = &me;
565     tname = me.name;
566     i = HUNTED_ISME;
567   }
568   else if (parc < 3 || BadPtr(parv[2]))
569   {
570     /* No target specified. Make one before propagating. */
571     parc = 2;
572     tname = parv[1];
573     if ((acptr = find_match_server(parv[1])) ||
574         ((acptr = FindClient(parv[1])) && !MyUser(acptr)))
575     {
576       if (IsUser(acptr))
577         parv[2] = acptr->user->server->name;
578       else
579         parv[2] = acptr->name;
580       parc = 3;
581       parv[3] = 0;
582       if ((i = hunt_server(IsServer(acptr), cptr, sptr,
583           "%s%s " TOK_TRACE " %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
584         return 0;
585     }
586     else
587       i = HUNTED_ISME;
588   }
589   else
590   {
591     /* Got "TRACE <tname> :<target>" */
592     parc = 3;
593     if (MyUser(sptr) || Protocol(cptr) < 10)
594       acptr = find_match_server(parv[2]);
595     else
596       acptr = FindNServer(parv[2]);
597     if ((i = hunt_server(0, cptr, sptr,
598         "%s%s " TOK_TRACE " %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
599       return 0;
600     tname = parv[1];
601   }
602
603   if (i == HUNTED_PASS)
604   {
605     if (!acptr)
606       acptr = next_client(GlobalClientList, tname);
607     else
608       acptr = acptr->from;
609     sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0],
610 #ifndef GODMODE
611         version, debugmode, tname, acptr ? acptr->from->name : "<No_match>");
612 #else /* GODMODE */
613         version, debugmode, tname, acptr ? acptr->from->name : "<No_match>",
614         (acptr && acptr->from->serv) ? acptr->from->serv->timestamp : 0);
615 #endif /* GODMODE */
616     return 0;
617   }
618
619   doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : 1;
620   wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
621   dow = wilds || doall;
622
623   /* Don't give (long) remote listings to lusers */
624   if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
625     return 0;
626
627   for (i = 0; i < MAXCONNECTIONS; i++)
628     link_s[i] = 0, link_u[i] = 0;
629
630   if (doall)
631   {
632     for (acptr = GlobalClientList; acptr; acptr = acptr->next) {
633       if (IsUser(acptr))
634         link_u[acptr->from->fd]++;
635       else if (IsServer(acptr))
636         link_s[acptr->from->fd]++;
637     }
638   }
639
640   /* report all direct connections */
641
642   for (i = 0; i <= HighestFd; i++)
643   {
644     unsigned int conClass;
645
646     if (!(acptr = LocalClientArray[i])) /* Local Connection? */
647       continue;
648     if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
649         !IsAnOper(acptr) && (acptr != sptr))
650       continue;
651     if (!doall && wilds && match(tname, acptr->name))
652       continue;
653     if (!dow && 0 != ircd_strcmp(tname, acptr->name))
654       continue;
655     conClass = get_client_class(acptr);
656
657     switch (acptr->status)
658     {
659       case STAT_CONNECTING:
660         sendto_one(sptr, rpl_str(RPL_TRACECONNECTING),
661                    me.name, parv[0], conClass, acptr->name);
662         cnt++;
663         break;
664       case STAT_HANDSHAKE:
665         sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE),
666                    me.name, parv[0], conClass, acptr->name);
667         cnt++;
668         break;
669       case STAT_ME:
670         break;
671       case STAT_UNKNOWN:
672       case STAT_UNKNOWN_USER:
673         sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
674                    me.name, parv[0], conClass, get_client_name(acptr, HIDE_IP));
675         cnt++;
676         break;
677       case STAT_UNKNOWN_SERVER:
678         sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
679                    me.name, parv[0], conClass, "Unknown Server");
680         cnt++;
681         break;
682       case STAT_USER:
683         /* Only opers see users if there is a wildcard
684            but anyone can see all the opers. */
685         if ((IsAnOper(sptr) && (MyUser(sptr) ||
686             !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr))
687         {
688           if (IsAnOper(acptr))
689             sendto_one(sptr, rpl_str(RPL_TRACEOPERATOR),
690                        me.name, parv[0], conClass, get_client_name(acptr, HIDE_IP),
691                        CurrentTime - acptr->lasttime);
692           else
693             sendto_one(sptr, rpl_str(RPL_TRACEUSER),
694                        me.name, parv[0], conClass, get_client_name(acptr, HIDE_IP),
695                        CurrentTime - acptr->lasttime);
696           cnt++;
697         }
698         break;
699         /*
700          * Connection is a server
701          *
702          * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
703          *
704          * class        Class the server is in
705          * nS           Number of servers reached via this link
706          * nC           Number of clients reached via this link
707          * name         Name of the server linked
708          * ConnBy       Who established this link
709          * last         Seconds since we got something from this link
710          * age          Seconds this link has been alive
711          *
712          * Additional comments etc......        -Cym-<cym@acrux.net>
713          */
714
715       case STAT_SERVER:
716         if (acptr->serv->user)
717           sendto_one(sptr, rpl_str(RPL_TRACESERVER),
718                      me.name, parv[0], conClass, link_s[i],
719                      link_u[i], acptr->name,
720                      (*acptr->serv->by) ? acptr->serv->by : "*",
721                      acptr->serv->user->username, acptr->serv->user->host,
722                      CurrentTime - acptr->lasttime,
723                      CurrentTime - acptr->serv->timestamp);
724         else
725           sendto_one(sptr, rpl_str(RPL_TRACESERVER),
726                      me.name, parv[0], conClass, link_s[i],
727                      link_u[i], acptr->name,
728                      (*acptr->serv->by) ?  acptr->serv->by : "*", "*",
729                      me.name, CurrentTime - acptr->lasttime,
730                      CurrentTime - acptr->serv->timestamp);
731         cnt++;
732         break;
733       default:                  /* We actually shouldn't come here, -msa */
734         sendto_one(sptr, rpl_str(RPL_TRACENEWTYPE), me.name, parv[0],
735                    get_client_name(acptr, HIDE_IP));
736         cnt++;
737         break;
738     }
739   }
740   /*
741    * Add these lines to summarize the above which can get rather long
742    * and messy when done remotely - Avalon
743    */
744   if (!IsAnOper(sptr) || !cnt)
745   {
746     if (!cnt)
747       /* let the user have some idea that its at the end of the trace */
748       sendto_one(sptr, rpl_str(RPL_TRACESERVER),
749                  me.name, parv[0], 0, link_s[me.fd],
750                  link_u[me.fd], "<No_match>", *(me.serv->by) ?
751                  me.serv->by : "*", "*", me.name, 0, 0);
752     return 0;
753   }
754   for (cltmp = FirstClass(); doall && cltmp; cltmp = NextClass(cltmp))
755     if (Links(cltmp) > 0)
756       sendto_one(sptr, rpl_str(RPL_TRACECLASS), me.name,
757                  parv[0], ConClass(cltmp), Links(cltmp));
758   return 0;
759 }
760
761   
762 #if 0
763 /*
764  * m_trace
765  *
766  * parv[0] = sender prefix
767  * parv[1] = nick or servername
768  * parv[2] = 'target' servername
769  */
770 int m_trace(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
771 {
772   int i;
773   struct Client *acptr;
774   struct ConfClass *cltmp;
775   char *tname;
776   int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
777   int cnt = 0, wilds, dow;
778
779   if (parc < 2 || BadPtr(parv[1]))
780   {
781     /* just "TRACE" without parameters. Must be from local client */
782     parc = 1;
783     acptr = &me;
784     tname = me.name;
785     i = HUNTED_ISME;
786   }
787   else if (parc < 3 || BadPtr(parv[2]))
788   {
789     /* No target specified. Make one before propagating. */
790     parc = 2;
791     tname = parv[1];
792     if ((acptr = find_match_server(parv[1])) ||
793         ((acptr = FindClient(parv[1])) && !MyUser(acptr)))
794     {
795       if (IsUser(acptr))
796         parv[2] = acptr->user->server->name;
797       else
798         parv[2] = acptr->name;
799       parc = 3;
800       parv[3] = 0;
801       if ((i = hunt_server(IsServer(acptr), cptr, sptr,
802           "%s%s " TOK_TRACE " %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
803         return 0;
804     }
805     else
806       i = HUNTED_ISME;
807   }
808   else
809   {
810     /* Got "TRACE <tname> :<target>" */
811     parc = 3;
812     if (MyUser(sptr) || Protocol(cptr) < 10)
813       acptr = find_match_server(parv[2]);
814     else
815       acptr = FindNServer(parv[2]);
816     if ((i = hunt_server(0, cptr, sptr,
817         "%s%s " TOK_TRACE " %s :%s", 2, parc, parv)) == HUNTED_NOSUCH)
818       return 0;
819     tname = parv[1];
820   }
821
822   if (i == HUNTED_PASS)
823   {
824     if (!acptr)
825       acptr = next_client(GlobalClientList, tname);
826     else
827       acptr = acptr->from;
828     sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0],
829 #ifndef GODMODE
830         version, debugmode, tname, acptr ? acptr->from->name : "<No_match>");
831 #else /* GODMODE */
832         version, debugmode, tname, acptr ? acptr->from->name : "<No_match>",
833         (acptr && acptr->from->serv) ? acptr->from->serv->timestamp : 0);
834 #endif /* GODMODE */
835     return 0;
836   }
837
838   doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : TRUE;
839   wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
840   dow = wilds || doall;
841
842   /* Don't give (long) remote listings to lusers */
843   if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
844     return 0;
845
846   for (i = 0; i < MAXCONNECTIONS; i++)
847     link_s[i] = 0, link_u[i] = 0;
848
849   if (doall)
850   {
851     for (acptr = GlobalClientList; acptr; acptr = acptr->next) {
852       if (IsUser(acptr))
853         link_u[acptr->from->fd]++;
854       else if (IsServer(acptr))
855         link_s[acptr->from->fd]++;
856     }
857   }
858
859   /* report all direct connections */
860
861   for (i = 0; i <= HighestFd; i++)
862   {
863     const char* name;
864     unsigned int conClass;
865
866     if (!(acptr = LocalClientArray[i])) /* Local Connection? */
867       continue;
868     if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
869         !IsAnOper(acptr) && (acptr != sptr))
870       continue;
871     if (!doall && wilds && match(tname, acptr->name))
872       continue;
873     if (!dow && 0 != ircd_strcmp(tname, acptr->name))
874       continue;
875     conClass = get_client_class(acptr);
876
877     switch (acptr->status)
878     {
879       case STAT_CONNECTING:
880         sendto_one(sptr, rpl_str(RPL_TRACECONNECTING),
881             me.name, parv[0], conClass, name);
882         cnt++;
883         break;
884       case STAT_HANDSHAKE:
885         sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE),
886             me.name, parv[0], conClass, name);
887         cnt++;
888         break;
889       case STAT_ME:
890         break;
891       case STAT_UNKNOWN:
892       case STAT_UNKNOWN_USER:
893       case STAT_UNKNOWN_SERVER:
894         sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
895             me.name, parv[0], conClass, name);
896         cnt++;
897         break;
898       case STAT_USER:
899         /* Only opers see users if there is a wildcard
900            but anyone can see all the opers. */
901         if ((IsAnOper(sptr) && (MyUser(sptr) ||
902             !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr))
903         {
904           if (IsAnOper(acptr))
905             sendto_one(sptr, rpl_str(RPL_TRACEOPERATOR),
906                 me.name, parv[0], conClass, name, CurrentTime - acptr->lasttime);
907           else
908             sendto_one(sptr, rpl_str(RPL_TRACEUSER),
909                 me.name, parv[0], conClass, name, CurrentTime - acptr->lasttime);
910           cnt++;
911         }
912         break;
913         /*
914          * Connection is a server
915          *
916          * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
917          *
918          * class        Class the server is in
919          * nS           Number of servers reached via this link
920          * nC           Number of clients reached via this link
921          * name         Name of the server linked
922          * ConnBy       Who established this link
923          * last         Seconds since we got something from this link
924          * age          Seconds this link has been alive
925          *
926          * Additional comments etc......        -Cym-<cym@acrux.net>
927          */
928
929       case STAT_SERVER:
930         if (acptr->serv->user)
931           sendto_one(sptr, rpl_str(RPL_TRACESERVER),
932               me.name, parv[0], conClass, link_s[i],
933               link_u[i], name, (*acptr->serv->by) ? acptr->serv->by : "*",
934               acptr->serv->user->username, acptr->serv->user->host,
935               CurrentTime - acptr->lasttime,
936               CurrentTime - acptr->serv->timestamp);
937         else
938           sendto_one(sptr, rpl_str(RPL_TRACESERVER),
939               me.name, parv[0], conClass, link_s[i],
940               link_u[i], name, (*acptr->serv->by) ?  acptr->serv->by : "*", "*",
941               me.name, CurrentTime - acptr->lasttime,
942               CurrentTime - acptr->serv->timestamp);
943         cnt++;
944         break;
945       default:                  /* We actually shouldn't come here, -msa */
946         sendto_one(sptr, rpl_str(RPL_TRACENEWTYPE), me.name, parv[0], name);
947         cnt++;
948         break;
949     }
950   }
951   /*
952    * Add these lines to summarize the above which can get rather long
953    * and messy when done remotely - Avalon
954    */
955   if (!IsAnOper(sptr) || !cnt)
956   {
957     if (!cnt)
958       /* let the user have some idea that its at the end of the trace */
959       sendto_one(sptr, rpl_str(RPL_TRACESERVER),
960           me.name, parv[0], 0, link_s[me.fd],
961           link_u[me.fd], "<No_match>", *(me.serv->by) ?
962           me.serv->by : "*", "*", me.name, 0, 0);
963     return 0;
964   }
965   for (cltmp = FirstClass(); doall && cltmp; cltmp = NextClass(cltmp))
966     if (Links(cltmp) > 0)
967       sendto_one(sptr, rpl_str(RPL_TRACECLASS), me.name,
968           parv[0], ConClass(cltmp), Links(cltmp));
969   return 0;
970 }
971 #endif /* 0 */
972