~ubuntu-branches/ubuntu/trusty/charybdis/trusty-proposed

« back to all changes in this revision

Viewing changes to modules/m_who.c

  • Committer: Package Import Robot
  • Author(s): Antoine Beaupré
  • Date: 2011-11-10 23:07:37 UTC
  • Revision ID: package-import@ubuntu.com-20111110230737-kqo6qsglp5oh02hr
Tags: upstream-3.3.0
Import upstream version 3.3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  ircd-ratbox: A slightly useful ircd.
 
3
 *  m_who.c: Shows who is on a channel.
 
4
 *
 
5
 *  Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
 
6
 *  Copyright (C) 1996-2002 Hybrid Development Team
 
7
 *  Copyright (C) 2002-2005 ircd-ratbox development team
 
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 2 of the License, or
 
12
 *  (at your option) 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 
22
 *  USA
 
23
 *
 
24
 *  $Id: m_who.c 3350 2007-04-02 22:03:08Z jilles $
 
25
 */
 
26
#include "stdinc.h"
 
27
#include "common.h"
 
28
#include "client.h"
 
29
#include "channel.h"
 
30
#include "hash.h"
 
31
#include "ircd.h"
 
32
#include "numeric.h"
 
33
#include "s_serv.h"
 
34
#include "send.h"
 
35
#include "match.h"
 
36
#include "s_conf.h"
 
37
#include "logger.h"
 
38
#include "msg.h"
 
39
#include "parse.h"
 
40
#include "modules.h"
 
41
#include "packet.h"
 
42
#include "s_newconf.h"
 
43
 
 
44
#define FIELD_CHANNEL    0x0001
 
45
#define FIELD_HOP        0x0002
 
46
#define FIELD_FLAGS      0x0004
 
47
#define FIELD_HOST       0x0008
 
48
#define FIELD_IP         0x0010
 
49
#define FIELD_IDLE       0x0020
 
50
#define FIELD_NICK       0x0040
 
51
#define FIELD_INFO       0x0080
 
52
#define FIELD_SERVER     0x0100
 
53
#define FIELD_QUERYTYPE  0x0200 /* cookie for client */
 
54
#define FIELD_USER       0x0400
 
55
#define FIELD_ACCOUNT    0x0800
 
56
#define FIELD_OPLEVEL    0x1000 /* meaningless and stupid, but whatever */
 
57
 
 
58
struct who_format
 
59
{
 
60
        int fields;
 
61
        const char *querytype;
 
62
};
 
63
 
 
64
static int m_who(struct Client *, struct Client *, int, const char **);
 
65
 
 
66
struct Message who_msgtab = {
 
67
        "WHO", 0, 0, 0, MFLG_SLOW,
 
68
        {mg_unreg, {m_who, 2}, mg_ignore, mg_ignore, mg_ignore, {m_who, 2}}
 
69
};
 
70
 
 
71
mapi_clist_av1 who_clist[] = { &who_msgtab, NULL };
 
72
DECLARE_MODULE_AV1(who, NULL, NULL, who_clist, NULL, NULL, "$Revision: 3350 $");
 
73
 
 
74
static void do_who_on_channel(struct Client *source_p, struct Channel *chptr,
 
75
                              int server_oper, int member,
 
76
                              struct who_format *fmt);
 
77
 
 
78
static void who_global(struct Client *source_p, const char *mask, int server_oper, int operspy, struct who_format *fmt);
 
79
 
 
80
static void do_who(struct Client *source_p,
 
81
                   struct Client *target_p, struct membership *msptr,
 
82
                   struct who_format *fmt);
 
83
 
 
84
 
 
85
/*
 
86
** m_who
 
87
**      parv[1] = nickname mask list
 
88
**      parv[2] = additional selection flag and format options
 
89
*/
 
90
static int
 
91
m_who(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
 
92
{
 
93
        static time_t last_used = 0;
 
94
        struct Client *target_p;
 
95
        struct membership *msptr;
 
96
        char *mask;
 
97
        rb_dlink_node *lp;
 
98
        struct Channel *chptr = NULL;
 
99
        int server_oper = parc > 2 ? (*parv[2] == 'o') : 0;     /* Show OPERS only */
 
100
        int member;
 
101
        int operspy = 0;
 
102
        struct who_format fmt;
 
103
        const char *s;
 
104
        char maskcopy[512];
 
105
 
 
106
        fmt.fields = 0;
 
107
        fmt.querytype = NULL;
 
108
        if (parc > 2 && (s = strchr(parv[2], '%')) != NULL)
 
109
        {
 
110
                s++;
 
111
                for (; *s != '\0'; s++)
 
112
                {
 
113
                        switch (*s)
 
114
                        {
 
115
                                case 'c': fmt.fields |= FIELD_CHANNEL; break;
 
116
                                case 'd': fmt.fields |= FIELD_HOP; break;
 
117
                                case 'f': fmt.fields |= FIELD_FLAGS; break;
 
118
                                case 'h': fmt.fields |= FIELD_HOST; break;
 
119
                                case 'i': fmt.fields |= FIELD_IP; break;
 
120
                                case 'l': fmt.fields |= FIELD_IDLE; break;
 
121
                                case 'n': fmt.fields |= FIELD_NICK; break;
 
122
                                case 'r': fmt.fields |= FIELD_INFO; break;
 
123
                                case 's': fmt.fields |= FIELD_SERVER; break;
 
124
                                case 't': fmt.fields |= FIELD_QUERYTYPE; break;
 
125
                                case 'u': fmt.fields |= FIELD_USER; break;
 
126
                                case 'a': fmt.fields |= FIELD_ACCOUNT; break;
 
127
                                case 'o': fmt.fields |= FIELD_OPLEVEL; break;
 
128
                                case ',':
 
129
                                          s++;
 
130
                                          fmt.querytype = s;
 
131
                                          s += strlen(s);
 
132
                                          s--;
 
133
                                          break;
 
134
                        }
 
135
                }
 
136
                if (EmptyString(fmt.querytype) || strlen(fmt.querytype) > 3)
 
137
                        fmt.querytype = "0";
 
138
        }
 
139
 
 
140
        rb_strlcpy(maskcopy, parv[1], sizeof maskcopy);
 
141
        mask = maskcopy;
 
142
 
 
143
        collapse(mask);
 
144
 
 
145
        /* '/who *' */
 
146
        if((*(mask + 1) == '\0') && (*mask == '*'))
 
147
        {
 
148
                if(source_p->user == NULL)
 
149
                        return 0;
 
150
 
 
151
                if((lp = source_p->user->channel.head) != NULL)
 
152
                {
 
153
                        msptr = lp->data;
 
154
                        do_who_on_channel(source_p, msptr->chptr, server_oper, YES, &fmt);
 
155
                }
 
156
 
 
157
                sendto_one(source_p, form_str(RPL_ENDOFWHO),
 
158
                           me.name, source_p->name, "*");
 
159
                return 0;
 
160
        }
 
161
 
 
162
        if(IsOperSpy(source_p) && *mask == '!')
 
163
        {
 
164
                mask++;
 
165
                operspy = 1;
 
166
 
 
167
                if(EmptyString(mask))
 
168
                {
 
169
                        sendto_one(source_p, form_str(RPL_ENDOFWHO),
 
170
                                        me.name, source_p->name, parv[1]);
 
171
                        return 0;
 
172
                }
 
173
        }
 
174
 
 
175
        /* '/who #some_channel' */
 
176
        if(IsChannelName(mask))
 
177
        {
 
178
                /* List all users on a given channel */
 
179
                chptr = find_channel(parv[1] + operspy);
 
180
                if(chptr != NULL)
 
181
                {
 
182
                        if(operspy)
 
183
                                report_operspy(source_p, "WHO", chptr->chname);
 
184
 
 
185
                        if(IsMember(source_p, chptr) || operspy)
 
186
                                do_who_on_channel(source_p, chptr, server_oper, YES, &fmt);
 
187
                        else if(!SecretChannel(chptr))
 
188
                                do_who_on_channel(source_p, chptr, server_oper, NO, &fmt);
 
189
                }
 
190
                sendto_one(source_p, form_str(RPL_ENDOFWHO),
 
191
                           me.name, source_p->name, parv[1] + operspy);
 
192
                return 0;
 
193
        }
 
194
 
 
195
        /* '/who nick' */
 
196
 
 
197
        if(((target_p = find_named_person(mask)) != NULL) &&
 
198
           (!server_oper || IsOper(target_p)))
 
199
        {
 
200
                int isinvis = 0;
 
201
 
 
202
                isinvis = IsInvisible(target_p);
 
203
                RB_DLINK_FOREACH(lp, target_p->user->channel.head)
 
204
                {
 
205
                        msptr = lp->data;
 
206
                        chptr = msptr->chptr;
 
207
 
 
208
                        member = IsMember(source_p, chptr);
 
209
 
 
210
                        if(isinvis && !member)
 
211
                                continue;
 
212
 
 
213
                        if(member || (!isinvis && PubChannel(chptr)))
 
214
                                break;
 
215
                }
 
216
 
 
217
                /* if we stopped midlist, lp->data is the membership for
 
218
                 * target_p of chptr
 
219
                 */
 
220
                if(lp != NULL)
 
221
                        do_who(source_p, target_p, lp->data, &fmt);
 
222
                else
 
223
                        do_who(source_p, target_p, NULL, &fmt);
 
224
 
 
225
                sendto_one(source_p, form_str(RPL_ENDOFWHO), 
 
226
                           me.name, source_p->name, mask);
 
227
                return 0;
 
228
        }
 
229
 
 
230
        if(!IsFloodDone(source_p))
 
231
                flood_endgrace(source_p);
 
232
 
 
233
        /* it has to be a global who at this point, limit it */
 
234
        if(!IsOper(source_p))
 
235
        {
 
236
                if((last_used + ConfigFileEntry.pace_wait) > rb_current_time())
 
237
                {
 
238
                        sendto_one(source_p, form_str(RPL_LOAD2HI),
 
239
                                        me.name, source_p->name, "WHO");
 
240
                        sendto_one(source_p, form_str(RPL_ENDOFWHO),
 
241
                                   me.name, source_p->name, "*");
 
242
                        return 0;
 
243
                }
 
244
                else
 
245
                        last_used = rb_current_time();
 
246
        }
 
247
 
 
248
        /* Note: operspy_dont_care_user_info does not apply to
 
249
         * who on channels */
 
250
        if(IsOperSpy(source_p) && ConfigFileEntry.operspy_dont_care_user_info)
 
251
                operspy = 1;
 
252
 
 
253
        /* '/who 0' for a global list.  this forces clients to actually
 
254
         * request a full list.  I presume its because of too many typos
 
255
         * with "/who" ;) --fl
 
256
         */
 
257
        if((*(mask + 1) == '\0') && (*mask == '0'))
 
258
                who_global(source_p, NULL, server_oper, 0, &fmt);
 
259
        else
 
260
                who_global(source_p, mask, server_oper, operspy, &fmt);
 
261
 
 
262
        sendto_one(source_p, form_str(RPL_ENDOFWHO),
 
263
                   me.name, source_p->name, mask);
 
264
 
 
265
        return 0;
 
266
}
 
267
 
 
268
/* who_common_channel
 
269
 * inputs       - pointer to client requesting who
 
270
 *              - pointer to channel member chain.
 
271
 *              - char * mask to match
 
272
 *              - int if oper on a server or not
 
273
 *              - pointer to int maxmatches
 
274
 *              - format options
 
275
 * output       - NONE
 
276
 * side effects - lists matching invisible clients on specified channel,
 
277
 *                marks matched clients.
 
278
 */
 
279
static void
 
280
who_common_channel(struct Client *source_p, struct Channel *chptr,
 
281
                   const char *mask, int server_oper, int *maxmatches,
 
282
                   struct who_format *fmt)
 
283
{
 
284
        struct membership *msptr;
 
285
        struct Client *target_p;
 
286
        rb_dlink_node *ptr;
 
287
 
 
288
        RB_DLINK_FOREACH(ptr, chptr->members.head)
 
289
        {
 
290
                msptr = ptr->data;
 
291
                target_p = msptr->client_p;
 
292
 
 
293
                if(!IsInvisible(target_p) || IsMarked(target_p))
 
294
                        continue;
 
295
 
 
296
                if(server_oper && !IsOper(target_p))
 
297
                        continue;
 
298
 
 
299
                SetMark(target_p);
 
300
 
 
301
                if(*maxmatches > 0)
 
302
                {
 
303
                        if((mask == NULL) ||
 
304
                                        match(mask, target_p->name) || match(mask, target_p->username) ||
 
305
                                        match(mask, target_p->host) || match(mask, target_p->servptr->name) ||
 
306
                                        (IsOper(source_p) && match(mask, target_p->orighost)) ||
 
307
                                        match(mask, target_p->info))
 
308
                        {
 
309
                                do_who(source_p, target_p, NULL, fmt);
 
310
                                --(*maxmatches);
 
311
                        }
 
312
                }
 
313
        }
 
314
}
 
315
 
 
316
/*
 
317
 * who_global
 
318
 *
 
319
 * inputs       - pointer to client requesting who
 
320
 *              - char * mask to match
 
321
 *              - int if oper on a server or not
 
322
 *              - int if operspy or not
 
323
 *              - format options
 
324
 * output       - NONE
 
325
 * side effects - do a global scan of all clients looking for match
 
326
 *                this is slightly expensive on EFnet ...
 
327
 *                marks assumed cleared for all clients initially
 
328
 *                and will be left cleared on return
 
329
 */
 
330
static void
 
331
who_global(struct Client *source_p, const char *mask, int server_oper, int operspy, struct who_format *fmt)
 
332
{
 
333
        struct membership *msptr;
 
334
        struct Client *target_p;
 
335
        rb_dlink_node *lp, *ptr;
 
336
        int maxmatches = 500;
 
337
 
 
338
        /* first, list all matching INvisible clients on common channels
 
339
         * if this is not an operspy who
 
340
         */
 
341
        if(!operspy)
 
342
        {
 
343
                RB_DLINK_FOREACH(lp, source_p->user->channel.head)
 
344
                {
 
345
                        msptr = lp->data;
 
346
                        who_common_channel(source_p, msptr->chptr, mask, server_oper, &maxmatches, fmt);
 
347
                }
 
348
        }
 
349
        else if (!ConfigFileEntry.operspy_dont_care_user_info)
 
350
                report_operspy(source_p, "WHO", mask);
 
351
 
 
352
        /* second, list all matching visible clients and clear all marks
 
353
         * on invisible clients
 
354
         * if this is an operspy who, list all matching clients, no need
 
355
         * to clear marks
 
356
         */
 
357
        RB_DLINK_FOREACH(ptr, global_client_list.head)
 
358
        {
 
359
                target_p = ptr->data;
 
360
                if(!IsPerson(target_p))
 
361
                        continue;
 
362
 
 
363
                if(IsInvisible(target_p) && !operspy)
 
364
                {
 
365
                        ClearMark(target_p);
 
366
                        continue;
 
367
                }
 
368
 
 
369
                if(server_oper && !IsOper(target_p))
 
370
                        continue;
 
371
 
 
372
                if(maxmatches > 0)
 
373
                {
 
374
                        if(!mask ||
 
375
                                        match(mask, target_p->name) || match(mask, target_p->username) ||
 
376
                                        match(mask, target_p->host) || match(mask, target_p->servptr->name) ||
 
377
                                        (IsOper(source_p) && match(mask, target_p->orighost)) ||
 
378
                                        match(mask, target_p->info))
 
379
                        {
 
380
                                do_who(source_p, target_p, NULL, fmt);
 
381
                                --maxmatches;
 
382
                        }
 
383
                }
 
384
        }
 
385
 
 
386
        if (maxmatches <= 0)
 
387
                sendto_one(source_p,
 
388
                        form_str(ERR_TOOMANYMATCHES),
 
389
                        me.name, source_p->name, "WHO");
 
390
}
 
391
 
 
392
/*
 
393
 * do_who_on_channel
 
394
 *
 
395
 * inputs       - pointer to client requesting who
 
396
 *              - pointer to channel to do who on
 
397
 *              - The "real name" of this channel
 
398
 *              - int if source_p is a server oper or not
 
399
 *              - int if client is member or not
 
400
 *              - format options
 
401
 * output       - NONE
 
402
 * side effects - do a who on given channel
 
403
 */
 
404
static void
 
405
do_who_on_channel(struct Client *source_p, struct Channel *chptr,
 
406
                  int server_oper, int member, struct who_format *fmt)
 
407
{
 
408
        struct Client *target_p;
 
409
        struct membership *msptr;
 
410
        rb_dlink_node *ptr;
 
411
 
 
412
        RB_DLINK_FOREACH(ptr, chptr->members.head)
 
413
        {
 
414
                msptr = ptr->data;
 
415
                target_p = msptr->client_p;
 
416
 
 
417
                if(server_oper && !IsOper(target_p))
 
418
                        continue;
 
419
 
 
420
                if(member || !IsInvisible(target_p))
 
421
                        do_who(source_p, target_p, msptr, fmt);
 
422
        }
 
423
}
 
424
 
 
425
/*
 
426
 * append_format
 
427
 *
 
428
 * inputs       - pointer to buffer
 
429
 *              - size of buffer
 
430
 *              - pointer to position
 
431
 *              - format string
 
432
 *              - arguments for format
 
433
 * output       - NONE
 
434
 * side effects - position incremented, possibly beyond size of buffer
 
435
 *                this allows detecting overflow
 
436
 */
 
437
static void
 
438
append_format(char *buf, size_t bufsize, size_t *pos, const char *fmt, ...)
 
439
{
 
440
        size_t max, result;
 
441
        va_list ap;
 
442
 
 
443
        max = *pos >= bufsize ? 0 : bufsize - *pos;
 
444
        va_start(ap, fmt);
 
445
        result = rb_vsnprintf(buf + *pos, max, fmt, ap);
 
446
        va_end(ap);
 
447
        *pos += result;
 
448
}
 
449
 
 
450
/*
 
451
 * do_who
 
452
 *
 
453
 * inputs       - pointer to client requesting who
 
454
 *              - pointer to client to do who on
 
455
 *              - channel membership or NULL
 
456
 *              - format options
 
457
 * output       - NONE
 
458
 * side effects - do a who on given person
 
459
 */
 
460
 
 
461
static void
 
462
do_who(struct Client *source_p, struct Client *target_p, struct membership *msptr, struct who_format *fmt)
 
463
{
 
464
        char status[16];
 
465
        char str[510 + 1]; /* linebuf.c will add \r\n */
 
466
        size_t pos;
 
467
        const char *q;
 
468
 
 
469
        rb_sprintf(status, "%c%s%s",
 
470
                   target_p->user->away ? 'G' : 'H', IsOper(target_p) ? "*" : "", msptr ? find_channel_status(msptr, fmt->fields || IsCapable(source_p, CLICAP_MULTI_PREFIX)) : "");
 
471
 
 
472
        if (fmt->fields == 0)
 
473
                sendto_one(source_p, form_str(RPL_WHOREPLY), me.name,
 
474
                           source_p->name, msptr ? msptr->chptr->chname : "*",
 
475
                           target_p->username, target_p->host,
 
476
                           target_p->servptr->name, target_p->name, status,
 
477
                           ConfigServerHide.flatten_links && !IsOper(source_p) && !IsExemptShide(source_p) ? 0 : target_p->hopcount, 
 
478
                           target_p->info);
 
479
        else
 
480
        {
 
481
                str[0] = '\0';
 
482
                pos = 0;
 
483
                append_format(str, sizeof str, &pos, ":%s %d %s",
 
484
                                me.name, RPL_WHOSPCRPL, source_p->name);
 
485
                if (fmt->fields & FIELD_QUERYTYPE)
 
486
                        append_format(str, sizeof str, &pos, " %s", fmt->querytype);
 
487
                if (fmt->fields & FIELD_CHANNEL)
 
488
                        append_format(str, sizeof str, &pos, " %s", msptr ? msptr->chptr->chname : "*");
 
489
                if (fmt->fields & FIELD_USER)
 
490
                        append_format(str, sizeof str, &pos, " %s", target_p->username);
 
491
                if (fmt->fields & FIELD_IP)
 
492
                {
 
493
                        if (show_ip(source_p, target_p) && !EmptyString(target_p->sockhost) && strcmp(target_p->sockhost, "0"))
 
494
                                append_format(str, sizeof str, &pos, " %s", target_p->sockhost);
 
495
                        else
 
496
                                append_format(str, sizeof str, &pos, " %s", "255.255.255.255");
 
497
                }
 
498
                if (fmt->fields & FIELD_HOST)
 
499
                        append_format(str, sizeof str, &pos, " %s", target_p->host);
 
500
                if (fmt->fields & FIELD_SERVER)
 
501
                        append_format(str, sizeof str, &pos, " %s", target_p->servptr->name);
 
502
                if (fmt->fields & FIELD_NICK)
 
503
                        append_format(str, sizeof str, &pos, " %s", target_p->name);
 
504
                if (fmt->fields & FIELD_FLAGS)
 
505
                        append_format(str, sizeof str, &pos, " %s", status);
 
506
                if (fmt->fields & FIELD_HOP)
 
507
                        append_format(str, sizeof str, &pos, " %d", ConfigServerHide.flatten_links && !IsOper(source_p) && !IsExemptShide(source_p) ? 0 : target_p->hopcount);
 
508
                if (fmt->fields & FIELD_IDLE)
 
509
                        append_format(str, sizeof str, &pos, " %d", (int)(MyClient(target_p) ? rb_current_time() - target_p->localClient->last : 0));
 
510
                if (fmt->fields & FIELD_ACCOUNT)
 
511
                {
 
512
                        /* display as in whois */
 
513
                        q = target_p->user->suser;
 
514
                        if (!EmptyString(q))
 
515
                        {
 
516
                                while(IsDigit(*q))
 
517
                                        q++;
 
518
                                if(*q == '\0')
 
519
                                        q = target_p->user->suser;
 
520
                        }
 
521
                        else
 
522
                                q = "0";
 
523
                        append_format(str, sizeof str, &pos, " %s", q);
 
524
                }
 
525
                if (fmt->fields & FIELD_OPLEVEL)
 
526
                        append_format(str, sizeof str, &pos, " %s", is_chanop(msptr) ? "999" : "n/a");
 
527
                if (fmt->fields & FIELD_INFO)
 
528
                        append_format(str, sizeof str, &pos, " :%s", target_p->info);
 
529
 
 
530
                if (pos >= sizeof str)
 
531
                {
 
532
                        static int warned = 0;
 
533
                        if (!warned)
 
534
                                sendto_realops_snomask(SNO_DEBUG, L_NETWIDE,
 
535
                                                "WHOX overflow while sending information about %s to %s",
 
536
                                                target_p->name, source_p->name);
 
537
                        warned = 1;
 
538
                }
 
539
                sendto_one(source_p, "%s", str);
 
540
        }
 
541
}