Author: Kev <klmitch@mit.edu>
[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 (cli_listing(sptr))            /* Already listing ? */
131   {
132     cli_listing(sptr)->chptr->mode.mode &= ~MODE_LISTED;
133     MyFree(cli_listing(sptr));
134     cli_listing(sptr) = 0;
135     send_reply(sptr, RPL_LISTEND);
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     send_reply(sptr, RPL_LISTUSAGE,
265                "Usage: \002/QUOTE LIST\002 \037parameters\037");
266     send_reply(sptr, RPL_LISTUSAGE,
267                "Where \037parameters\037 is a space or comma seperated "
268                "list of one or more of:");
269     send_reply(sptr, RPL_LISTUSAGE,
270                " \002<\002\037max_users\037    ; Show all channels with less "
271                "than \037max_users\037.");
272     send_reply(sptr, RPL_LISTUSAGE,
273                " \002>\002\037min_users\037    ; Show all channels with more "
274                "than \037min_users\037.");
275     send_reply(sptr, RPL_LISTUSAGE,
276                " \002C<\002\037max_minutes\037 ; Channels that exist less "
277                "than \037max_minutes\037.");
278     send_reply(sptr, RPL_LISTUSAGE,
279                " \002C>\002\037min_minutes\037 ; Channels that exist more "
280                "than \037min_minutes\037.");
281     send_reply(sptr, RPL_LISTUSAGE,
282                " \002T<\002\037max_minutes\037 ; Channels with a topic last "
283                "set less than \037max_minutes\037 ago.");
284     send_reply(sptr, RPL_LISTUSAGE,
285                " \002T>\002\037min_minutes\037 ; Channels with a topic last "
286                "set more than \037min_minutes\037 ago.");
287     send_reply(sptr, RPL_LISTUSAGE,
288                "Example: LIST <3,>1,C<10,T>0  ; 2 users, younger than 10 "
289                "min., topic set.");
290     return 0;
291   }
292
293   send_reply(sptr, RPL_LISTSTART);
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       cli_listing(sptr) = (struct ListingArgs*) MyMalloc(sizeof(struct ListingArgs));
301       assert(0 != cli_listing(sptr));
302       memcpy(cli_listing(sptr), &args, sizeof(struct ListingArgs));
303       if ((cli_listing(sptr)->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(cli_listing(sptr));
310       cli_listing(sptr) = 0;
311     }
312     send_reply(sptr, RPL_LISTEND);
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) && cli_user(sptr))
320       send_reply(sptr, RPL_LIST, chptr->chname,
321                  chptr->users - number_of_zombies(chptr), chptr->topic);
322   }
323
324   send_reply(sptr, RPL_LISTEND);
325   return 0;
326 }
327
328
329 #if 0
330 /*
331  * m_list
332  *
333  * parv[0] = sender prefix
334  * parv[1] = channel list or user/time limit
335  * parv[2...] = more user/time limits
336  */
337 int m_list(struct Client* cptr, struct Client *sptr, int parc, char *parv[])
338 {
339   struct Channel *chptr;
340   char *name, *p = 0;
341   int show_usage = 0, show_channels = 0, param;
342   struct ListingArgs args = {
343     2147483647,                 /* max_time */
344     0,                          /* min_time */
345     4294967295U,                /* max_users */
346     0,                          /* min_users */
347     0,                          /* topic_limits */
348     2147483647,                 /* max_topic_time */
349     0,                          /* min_topic_time */
350     0                        /* chptr */
351   };
352
353   if (sptr->listing)            /* Already listing ? */
354   {
355     sptr->listing->chptr->mode.mode &= ~MODE_LISTED;
356     MyFree(sptr->listing);
357     sptr->listing = 0;
358     sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, sptr->name); /* XXX DEAD */
359     if (parc < 2)
360       return 0;                 /* Let LIST abort a listing. */
361   }
362
363   if (parc < 2)                 /* No arguments given to /LIST ? */
364   {
365 #ifdef DEFAULT_LIST_PARAM
366     static char *defparv[MAXPARA + 1];
367     static int defparc = 0;
368     static char lp[] = DEFAULT_LIST_PARAM;
369     int i;
370
371     if (!defparc)
372     {
373       char *s = lp, *t;
374
375       defparc = 1;
376       defparv[defparc++] = t = strtok(s, " ");
377       while (t && defparc < MAXPARA)
378       {
379         if ((t = strtok(0, " ")))
380           defparv[defparc++] = t;
381       }
382     }
383     for (i = 1; i < defparc; i++)
384       parv[i] = defparv[i];
385     parv[i] = 0;
386     parc = defparc;
387 #endif /* DEFAULT_LIST_PARAM */
388   }
389
390   /* Decode command */
391   for (param = 1; !show_usage && parv[param]; param++)
392   {
393     char *p = parv[param];
394     do
395     {
396       int is_time = 0;
397       switch (*p)
398       {
399         case 'T':
400         case 't':
401           is_time++;
402           args.topic_limits = 1;
403           /* Fall through */
404         case 'C':
405         case 'c':
406           is_time++;
407           p++;
408           if (*p != '<' && *p != '>')
409           {
410             show_usage = 1;
411             break;
412           }
413           /* Fall through */
414         case '<':
415         case '>':
416         {
417           p++;
418           if (!IsDigit(*p))
419             show_usage = 1;
420           else
421           {
422             if (is_time)
423             {
424               time_t val = atoi(p);
425               if (p[-1] == '<')
426               {
427                 if (val < 80000000)     /* Toggle UTC/offset */
428                 {
429                   /*
430                    * Demands that
431                    * 'TStime() - chptr->creationtime < val * 60'
432                    * Which equals
433                    * 'chptr->creationtime > TStime() - val * 60'
434                    */
435                   if (is_time == 1)
436                     args.min_time = TStime() - val * 60;
437                   else
438                     args.min_topic_time = TStime() - val * 60;
439                 }
440                 else if (is_time == 1)  /* Creation time in UTC was entered */
441                   args.max_time = val;
442                 else            /* Topic time in UTC was entered */
443                   args.max_topic_time = val;
444               }
445               else if (val < 80000000)
446               {
447                 if (is_time == 1)
448                   args.max_time = TStime() - val * 60;
449                 else
450                   args.max_topic_time = TStime() - val * 60;
451               }
452               else if (is_time == 1)
453                 args.min_time = val;
454               else
455                 args.min_topic_time = val;
456             }
457             else if (p[-1] == '<')
458               args.max_users = atoi(p);
459             else
460               args.min_users = atoi(p);
461             if ((p = strchr(p, ',')))
462               p++;
463           }
464           break;
465         }
466         default:
467           if (!IsChannelName(p))
468           {
469             show_usage = 1;
470             break;
471           }
472           if (parc != 2)        /* Don't allow a mixture of channels with <,> */
473             show_usage = 1;
474           show_channels = 1;
475           p = 0;
476           break;
477       }
478     }
479     while (!show_usage && p);   /* p points after comma, or is NULL */
480   }
481
482   if (show_usage)
483   {
484     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
485         "Usage: \002/QUOTE LIST\002 \037parameters\037");
486     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
487         "Where \037parameters\037 is a space or comma seperated "
488         "list of one or more of:");
489     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
490         " \002<\002\037max_users\037    ; Show all channels with less "
491         "than \037max_users\037.");
492     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
493         " \002>\002\037min_users\037    ; Show all channels with more "
494         "than \037min_users\037.");
495     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
496         " \002C<\002\037max_minutes\037 ; Channels that exist less "
497         "than \037max_minutes\037.");
498     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
499         " \002C>\002\037min_minutes\037 ; Channels that exist more "
500         "than \037min_minutes\037.");
501     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
502         " \002T<\002\037max_minutes\037 ; Channels with a topic last "
503         "set less than \037max_minutes\037 ago.");
504     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
505         " \002T>\002\037min_minutes\037 ; Channels with a topic last "
506         "set more than \037min_minutes\037 ago.");
507     sendto_one(sptr, rpl_str(RPL_LISTUSAGE), me.name, parv[0], /* XXX DEAD */
508         "Example: LIST <3,>1,C<10,T>0  ; 2 users, younger than 10 min., "
509         "topic set.");
510     return 0;
511   }
512
513   sendto_one(sptr, rpl_str(RPL_LISTSTART), me.name, parv[0]); /* XXX DEAD */
514
515   if (!show_channels)
516   {
517     if (args.max_users > args.min_users + 1 && args.max_time > args.min_time &&
518         args.max_topic_time > args.min_topic_time)      /* Sanity check */
519     {
520       sptr->listing = (struct ListingArgs*) MyMalloc(sizeof(struct ListingArgs));
521       assert(0 != sptr->listing);
522       memcpy(sptr->listing, &args, sizeof(struct ListingArgs));
523       if ((sptr->listing->chptr = GlobalChannelList)) {
524         int m = GlobalChannelList->mode.mode & MODE_LISTED;
525         list_next_channels(sptr, 64);
526         GlobalChannelList->mode.mode |= m;
527         return 0;
528       }
529       MyFree(sptr->listing);
530       sptr->listing = 0;
531     }
532     sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]); /* XXX DEAD */
533     return 0;
534   }
535
536   for (; (name = ircd_strtok(&p, parv[1], ",")); parv[1] = 0)
537   {
538     chptr = FindChannel(name);
539     if (chptr && ShowChannel(sptr, chptr) && sptr->user)
540       sendto_one(sptr, rpl_str(RPL_LIST), me.name, parv[0], /* XXX DEAD */
541           ShowChannel(sptr, chptr) ? chptr->chname : "*",
542           chptr->users - number_of_zombies(chptr), chptr->topic);
543   }
544
545   sendto_one(sptr, rpl_str(RPL_LISTEND), me.name, parv[0]); /* XXX DEAD */
546   return 0;
547 }
548 #endif /* 0 */
549