Author: Bleep <tomh@inxpress.net>
[ircu2.10.12-pk.git] / ircd / m_list.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_list.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 "channel.h"
91 #include "client.h"
92 #include "hash.h"
93 #include "ircd.h"
94 #include "ircd_alloc.h"
95 #include "ircd_chattr.h"
96 #include "ircd_reply.h"
97 #include "ircd_string.h"
98 #include "msg.h"
99 #include "numeric.h"
100 #include "numnicks.h"
101 #include "send.h"
102
103 #include <assert.h>
104 #include <stdlib.h>
105 #include <string.h>
106
107 /*
108  * m_list - generic message handler
109  *
110  * parv[0] = sender prefix
111  * parv[1] = channel list or user/time limit
112  * parv[2...] = more user/time limits
113  */
114 int m_list(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
115 {
116   struct Channel *chptr;
117   char *name, *p = 0;
118   int show_usage = 0, show_channels = 0, param;
119   struct ListingArgs args = {
120     2147483647,                 /* max_time */
121     0,                          /* min_time */
122     4294967295U,                /* max_users */
123     0,                          /* min_users */
124     0,                          /* topic_limits */
125     2147483647,                 /* max_topic_time */
126     0,                          /* min_topic_time */
127     0                        /* chptr */
128   };
129
130   if (sptr->listing)            /* Already listing ? */
131   {
132     sptr->listing->chptr->mode.mode &= ~MODE_LISTED;
133     MyFree(sptr->listing);
134     sptr->listing = 0;
135     sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, sptr->name);
136     if (parc < 2)
137       return 0;                 /* Let LIST abort a listing. */
138   }
139
140   if (parc < 2)                 /* No arguments given to /LIST ? */
141   {
142 #ifdef DEFAULT_LIST_PARAM
143     static char *defparv[MAXPARA + 1];
144     static int defparc = 0;
145     static char lp[] = DEFAULT_LIST_PARAM;
146     int i;
147
148     /*
149      * XXX - strtok used
150      */
151     if (!defparc)
152     {
153       char *s = lp, *t;
154
155       defparc = 1;
156       defparv[defparc++] = t = strtok(s, " ");
157       while (t && defparc < MAXPARA)
158       {
159         if ((t = strtok(0, " ")))
160           defparv[defparc++] = t;
161       }
162     }
163     for (i = 1; i < defparc; i++)
164       parv[i] = defparv[i];
165     parv[i] = 0;
166     parc = defparc;
167 #endif /* DEFAULT_LIST_PARAM */
168   }
169
170   /* Decode command */
171   for (param = 1; !show_usage && parv[param]; param++)
172   {
173     char *p = parv[param];
174     do
175     {
176       int is_time = 0;
177       switch (*p)
178       {
179         case 'T':
180         case 't':
181           is_time++;
182           args.topic_limits = 1;
183           /* Fall through */
184         case 'C':
185         case 'c':
186           is_time++;
187           p++;
188           if (*p != '<' && *p != '>')
189           {
190             show_usage = 1;
191             break;
192           }
193           /* Fall through */
194         case '<':
195         case '>':
196         {
197           p++;
198           if (!IsDigit(*p))
199             show_usage = 1;
200           else
201           {
202             if (is_time)
203             {
204               time_t val = atoi(p);
205               if (p[-1] == '<')
206               {
207                 if (val < 80000000)     /* Toggle UTC/offset */
208                 {
209                   /*
210                    * Demands that
211                    * 'TStime() - chptr->creationtime < val * 60'
212                    * Which equals
213                    * 'chptr->creationtime > TStime() - val * 60'
214                    */
215                   if (is_time == 1)
216                     args.min_time = TStime() - val * 60;
217                   else
218                     args.min_topic_time = TStime() - val * 60;
219                 }
220                 else if (is_time == 1)  /* Creation time in UTC was entered */
221                   args.max_time = val;
222                 else            /* Topic time in UTC was entered */
223                   args.max_topic_time = val;
224               }
225               else if (val < 80000000)
226               {
227                 if (is_time == 1)
228                   args.max_time = TStime() - val * 60;
229                 else
230                   args.max_topic_time = TStime() - val * 60;
231               }
232               else if (is_time == 1)
233                 args.min_time = val;
234               else
235                 args.min_topic_time = val;
236             }
237             else if (p[-1] == '<')
238               args.max_users = atoi(p);
239             else
240               args.min_users = atoi(p);
241             if ((p = strchr(p, ',')))
242               p++;
243           }
244           break;
245         }
246         default:
247           if (!IsChannelName(p))
248           {
249             show_usage = 1;
250             break;
251           }
252           if (parc != 2)        /* Don't allow a mixture of channels with <,> */
253             show_usage = 1;
254           show_channels = 1;
255           p = 0;
256           break;
257       }
258     }
259     while (!show_usage && p);   /* p points after comma, or is NULL */
260   }
261
262   if (show_usage)
263   {
264     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
265         "Usage: \002/QUOTE LIST\002 \037parameters\037");
266     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
267         "Where \037parameters\037 is a space or comma seperated "
268         "list of one or more of:");
269     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
270         " \002<\002\037max_users\037    ; Show all channels with less "
271         "than \037max_users\037.");
272     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
273         " \002>\002\037min_users\037    ; Show all channels with more "
274         "than \037min_users\037.");
275     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
276         " \002C<\002\037max_minutes\037 ; Channels that exist less "
277         "than \037max_minutes\037.");
278     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
279         " \002C>\002\037min_minutes\037 ; Channels that exist more "
280         "than \037min_minutes\037.");
281     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
282         " \002T<\002\037max_minutes\037 ; Channels with a topic last "
283         "set less than \037max_minutes\037 ago.");
284     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
285         " \002T>\002\037min_minutes\037 ; Channels with a topic last "
286         "set more than \037min_minutes\037 ago.");
287     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
288         "Example: LIST <3,>1,C<10,T>0  ; 2 users, younger than 10 min., "
289         "topic set.");
290     return 0;
291   }
292
293   sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, parv[0]);
294
295   if (!show_channels)
296   {
297     if (args.max_users > args.min_users + 1 && args.max_time > args.min_time &&
298         args.max_topic_time > args.min_topic_time)      /* Sanity check */
299     {
300       sptr->listing = (struct ListingArgs*) MyMalloc(sizeof(struct ListingArgs));
301       assert(0 != sptr->listing);
302       memcpy(sptr->listing, &args, sizeof(struct ListingArgs));
303       if ((sptr->listing->chptr = GlobalChannelList)) {
304         int m = GlobalChannelList->mode.mode & MODE_LISTED;
305         list_next_channels(sptr, 64);
306         GlobalChannelList->mode.mode |= m;
307         return 0;
308       }
309       MyFree(sptr->listing);
310       sptr->listing = 0;
311     }
312     sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
313     return 0;
314   }
315
316   for (; (name = ircd_strtok(&p, parv[1], ",")); parv[1] = 0)
317   {
318     chptr = FindChannel(name);
319     if (chptr && ShowChannel(sptr, chptr) && sptr->user)
320       sendto_one(sptr, rpl_str(RPL_LIST), me.name, parv[0],
321           chptr->chname,
322           chptr->users - number_of_zombies(chptr), chptr->topic);
323   }
324
325   sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
326   return 0;
327 }
328
329
330 #if 0
331 /*
332  * m_list
333  *
334  * parv[0] = sender prefix
335  * parv[1] = channel list or user/time limit
336  * parv[2...] = more user/time limits
337  */
338 int m_list(struct Client* cptr, struct Client *sptr, int parc, char *parv[])
339 {
340   struct Channel *chptr;
341   char *name, *p = 0;
342   int show_usage = 0, show_channels = 0, param;
343   struct ListingArgs args = {
344     2147483647,                 /* max_time */
345     0,                          /* min_time */
346     4294967295U,                /* max_users */
347     0,                          /* min_users */
348     0,                          /* topic_limits */
349     2147483647,                 /* max_topic_time */
350     0,                          /* min_topic_time */
351     0                        /* chptr */
352   };
353
354   if (sptr->listing)            /* Already listing ? */
355   {
356     sptr->listing->chptr->mode.mode &= ~MODE_LISTED;
357     MyFree(sptr->listing);
358     sptr->listing = 0;
359     sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, sptr->name);
360     if (parc < 2)
361       return 0;                 /* Let LIST abort a listing. */
362   }
363
364   if (parc < 2)                 /* No arguments given to /LIST ? */
365   {
366 #ifdef DEFAULT_LIST_PARAM
367     static char *defparv[MAXPARA + 1];
368     static int defparc = 0;
369     static char lp[] = DEFAULT_LIST_PARAM;
370     int i;
371
372     if (!defparc)
373     {
374       char *s = lp, *t;
375
376       defparc = 1;
377       defparv[defparc++] = t = strtok(s, " ");
378       while (t && defparc < MAXPARA)
379       {
380         if ((t = strtok(0, " ")))
381           defparv[defparc++] = t;
382       }
383     }
384     for (i = 1; i < defparc; i++)
385       parv[i] = defparv[i];
386     parv[i] = 0;
387     parc = defparc;
388 #endif /* DEFAULT_LIST_PARAM */
389   }
390
391   /* Decode command */
392   for (param = 1; !show_usage && parv[param]; param++)
393   {
394     char *p = parv[param];
395     do
396     {
397       int is_time = 0;
398       switch (*p)
399       {
400         case 'T':
401         case 't':
402           is_time++;
403           args.topic_limits = 1;
404           /* Fall through */
405         case 'C':
406         case 'c':
407           is_time++;
408           p++;
409           if (*p != '<' && *p != '>')
410           {
411             show_usage = 1;
412             break;
413           }
414           /* Fall through */
415         case '<':
416         case '>':
417         {
418           p++;
419           if (!IsDigit(*p))
420             show_usage = 1;
421           else
422           {
423             if (is_time)
424             {
425               time_t val = atoi(p);
426               if (p[-1] == '<')
427               {
428                 if (val < 80000000)     /* Toggle UTC/offset */
429                 {
430                   /*
431                    * Demands that
432                    * 'TStime() - chptr->creationtime < val * 60'
433                    * Which equals
434                    * 'chptr->creationtime > TStime() - val * 60'
435                    */
436                   if (is_time == 1)
437                     args.min_time = TStime() - val * 60;
438                   else
439                     args.min_topic_time = TStime() - val * 60;
440                 }
441                 else if (is_time == 1)  /* Creation time in UTC was entered */
442                   args.max_time = val;
443                 else            /* Topic time in UTC was entered */
444                   args.max_topic_time = val;
445               }
446               else if (val < 80000000)
447               {
448                 if (is_time == 1)
449                   args.max_time = TStime() - val * 60;
450                 else
451                   args.max_topic_time = TStime() - val * 60;
452               }
453               else if (is_time == 1)
454                 args.min_time = val;
455               else
456                 args.min_topic_time = val;
457             }
458             else if (p[-1] == '<')
459               args.max_users = atoi(p);
460             else
461               args.min_users = atoi(p);
462             if ((p = strchr(p, ',')))
463               p++;
464           }
465           break;
466         }
467         default:
468           if (!IsChannelName(p))
469           {
470             show_usage = 1;
471             break;
472           }
473           if (parc != 2)        /* Don't allow a mixture of channels with <,> */
474             show_usage = 1;
475           show_channels = 1;
476           p = 0;
477           break;
478       }
479     }
480     while (!show_usage && p);   /* p points after comma, or is NULL */
481   }
482
483   if (show_usage)
484   {
485     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
486         "Usage: \002/QUOTE LIST\002 \037parameters\037");
487     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
488         "Where \037parameters\037 is a space or comma seperated "
489         "list of one or more of:");
490     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
491         " \002<\002\037max_users\037    ; Show all channels with less "
492         "than \037max_users\037.");
493     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
494         " \002>\002\037min_users\037    ; Show all channels with more "
495         "than \037min_users\037.");
496     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
497         " \002C<\002\037max_minutes\037 ; Channels that exist less "
498         "than \037max_minutes\037.");
499     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
500         " \002C>\002\037min_minutes\037 ; Channels that exist more "
501         "than \037min_minutes\037.");
502     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
503         " \002T<\002\037max_minutes\037 ; Channels with a topic last "
504         "set less than \037max_minutes\037 ago.");
505     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
506         " \002T>\002\037min_minutes\037 ; Channels with a topic last "
507         "set more than \037min_minutes\037 ago.");
508     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0],
509         "Example: LIST <3,>1,C<10,T>0  ; 2 users, younger than 10 min., "
510         "topic set.");
511     return 0;
512   }
513
514   sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, parv[0]);
515
516   if (!show_channels)
517   {
518     if (args.max_users > args.min_users + 1 && args.max_time > args.min_time &&
519         args.max_topic_time > args.min_topic_time)      /* Sanity check */
520     {
521       sptr->listing = (struct ListingArgs*) MyMalloc(sizeof(struct ListingArgs));
522       assert(0 != sptr->listing);
523       memcpy(sptr->listing, &args, sizeof(struct ListingArgs));
524       if ((sptr->listing->chptr = GlobalChannelList)) {
525         int m = GlobalChannelList->mode.mode & MODE_LISTED;
526         list_next_channels(sptr, 64);
527         GlobalChannelList->mode.mode |= m;
528         return 0;
529       }
530       MyFree(sptr->listing);
531       sptr->listing = 0;
532     }
533     sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
534     return 0;
535   }
536
537   for (; (name = ircd_strtok(&p, parv[1], ",")); parv[1] = 0)
538   {
539     chptr = FindChannel(name);
540     if (chptr && ShowChannel(sptr, chptr) && sptr->user)
541       sendto_one(sptr, rpl_str(RPL_LIST), me.name, parv[0],
542           ShowChannel(sptr, chptr) ? chptr->chname : "*",
543           chptr->users - number_of_zombies(chptr), chptr->topic);
544   }
545
546   sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]);
547   return 0;
548 }
549 #endif /* 0 */
550