chiark / gitweb /
more manpage, remove -h option
[innduct.git] / nnrpd / list.c
1 /*  $Id: list.c 7731 2008-04-06 08:40:29Z iulius $
2 **
3 **  List commands.
4 */
5
6 #include "config.h"
7 #include "clibrary.h"
8
9 #include "nnrpd.h"
10 #include "ov.h"
11 #include "inn/innconf.h"
12 #include "inn/messages.h"
13
14 typedef struct _LISTINFO {
15     const char *method;
16     const char * File;
17     void (*impl)(struct _LISTINFO *);
18     bool         Required;
19     const char * Items;
20     const char * Format;
21 } LISTINFO;
22
23 static void cmd_list_schema(LISTINFO *lp);
24 static void cmd_list_extensions(LISTINFO *lp);
25
26 static LISTINFO         INFOactive = {
27     "active", _PATH_ACTIVE, NULL, true, "active newsgroups",
28     "Newsgroups in form \"group high low flags\""
29 };
30 static LISTINFO         INFOactivetimes = {
31     "active.times", _PATH_ACTIVETIMES, NULL, false, "creation times",
32     "Group creations in form \"name time who\""
33 };
34 static LISTINFO         INFOdistribs = {
35     "distributions", _PATH_NNRPDIST, NULL, false, "newsgroup distributions",
36     "Distributions in form \"area description\""
37 };
38 static LISTINFO               INFOsubs = {
39     "subscriptions", _PATH_NNRPSUBS, NULL, false, "automatic group subscriptions",
40     "Subscriptions in form \"group\""
41 };
42 static LISTINFO         INFOdistribpats = {
43     "distrib.pats", _PATH_DISTPATS, NULL, false, "distribution patterns",
44     "Default distributions in form \"weight:pattern:value\""
45 };
46 static LISTINFO         INFOextensions = {
47     "extensions", NULL, cmd_list_extensions, false, "supported extensions",
48     "Supported NNTP extensions"
49 };
50 static LISTINFO         INFOgroups = {
51     "newsgroups", _PATH_NEWSGROUPS, NULL, false, "newsgroup descriptions",
52     "Descriptions in form \"group description\""
53 };
54 static LISTINFO         INFOmoderators = {
55     "moderators", _PATH_MODERATORS, NULL, false, "moderator patterns",
56     "Newsgroup moderators in form \"group-pattern:mail-address-pattern\""
57 };
58 static LISTINFO         INFOschema = {
59     "overview.fmt", NULL, cmd_list_schema, true, "overview format",
60     "Order of fields in overview database"
61 };
62 static LISTINFO         INFOmotd = {
63     "motd", _PATH_MOTD, NULL, false, "motd",
64     "Message of the day text"
65 };
66
67 static LISTINFO *info[] = {
68     &INFOactive,
69     &INFOactivetimes,
70     &INFOdistribs,
71     &INFOsubs,
72     &INFOdistribpats,
73     &INFOextensions,
74     &INFOgroups,
75     &INFOmoderators,
76     &INFOschema,
77     &INFOmotd,
78 };
79
80
81 /*
82 **  List the overview schema
83 */
84 static void
85 cmd_list_schema(LISTINFO *lp)
86 {
87     const struct cvector *standard;
88     unsigned int i;
89
90     Reply("%d %s.\r\n", NNTP_LIST_FOLLOWS_VAL, lp->Format);
91     standard = overview_fields();
92     for (i = 0; i < standard->count; ++i) {
93         Printf("%s:\r\n", standard->strings[i]);
94     }
95     for (i = 0; i < OVextra->count; ++i) {
96         Printf("%s:full\r\n", OVextra->strings[i]);
97     }
98     Printf(".\r\n");
99 }
100
101
102 /*
103 **  List supported extensions
104 */
105 static void
106 cmd_list_extensions(LISTINFO *lp)
107 {
108     Reply("%d %s.\r\n", NNTP_SLAVEOK_VAL, lp->Format);
109     if (PERMauthorized != true)
110         Printf("AUTHINFO USER\r\n");
111     Printf("LISTGROUP\r\n");
112     Printf(".\r\n");
113 }
114
115
116 /*
117 **  List a single newsgroup.  Called by LIST ACTIVE with a single argument.
118 **  This is quicker than parsing the whole active file, but only works with
119 **  single groups.  It also doesn't work for aliased groups, since overview
120 **  doesn't know what group the group is aliased to (yet).  Returns whether we
121 **  were able to answer the command.
122 */
123 static bool
124 CMD_list_single(char *group)
125 {
126     char *grplist[2] = { NULL, NULL };
127     int lo, hi, flag;
128
129     if (PERMspecified) {
130         grplist[0] = group;
131         if (!PERMmatch(PERMreadlist, grplist))
132             return false;
133     }
134     if (OVgroupstats(group, &lo, &hi, NULL, &flag) && flag != '=') {
135         Reply("%d %s.\r\n", NNTP_LIST_FOLLOWS_VAL, INFOactive.Format);
136         Printf("%s %010u %010u %c\r\n.\r\n", group, hi, lo, flag);
137         return true;
138     }
139     return false;
140 }
141
142
143 /*
144 **  List active newsgroups, newsgroup descriptions, and distributions.
145 */
146 void
147 CMDlist(int ac, char *av[])
148 {
149     QIOSTATE    *qp;
150     char        *p;
151     char        *save;
152     char        *path;
153     char                *q;
154     char                *grplist[2];
155     LISTINFO            *lp;
156     char                *wildarg = NULL;
157     char                savec;
158     unsigned int i;
159
160     p = av[1];
161     if (p == NULL) {
162         lp = &INFOactive;
163     } else {
164         lp = NULL;
165         for (i = 0; i < ARRAY_SIZE(info); ++i) {
166             if (strcasecmp(p, info[i]->method) == 0) {
167                 lp = info[i];
168                 break;
169             }
170         }
171     }
172     if (lp == NULL) {
173         Reply("%s\r\n", NNTP_SYNTAX_USE);
174         return;
175     }
176     if (lp == &INFOactive) {
177         if (ac == 3) {
178             wildarg = av[2];
179             if (CMD_list_single(wildarg))
180                 return;
181         }
182     } else if (lp == &INFOgroups || lp == &INFOactivetimes) {
183         if (ac == 3)
184             wildarg = av[2];
185     }
186
187     if (ac > 2 && !wildarg) {
188         Reply("%s\r\n", NNTP_SYNTAX_USE);
189         return;
190     }
191
192     if (lp->impl != NULL) {
193         lp->impl(lp);
194         return;
195     }
196
197     path = innconf->pathetc;
198     if ((strstr(lp->File, "active") != NULL) ||
199         (strstr(lp->File, "newsgroups") != NULL))
200         path = innconf->pathdb;
201     if (strchr(lp->File, '/') != NULL)
202         path = "";
203     path = concatpath(path, lp->File);
204     qp = QIOopen(path);
205     free(path);
206     if (qp == NULL) {
207         Reply("%d No list of %s available.\r\n",
208               NNTP_TEMPERR_VAL, lp->Items);
209             if (lp->Required || errno != ENOENT) {
210             syslog(L_ERROR, "%s cant fopen %s %m", ClientHost, lp->File);
211         }
212         return;
213     }
214
215     Reply("%d %s.\r\n", NNTP_LIST_FOLLOWS_VAL, lp->Format);
216     if (!PERMspecified) {
217         /* Optmize for unlikely case of no permissions and false default. */
218         QIOclose(qp);
219         Printf(".\r\n");
220         return;
221     }
222
223     /* Set up group list terminator. */
224     grplist[1] = NULL;
225
226     /* Read lines, ignore long ones. */
227     while ((p = QIOread(qp)) != NULL) {
228         if (lp == &INFOmotd) {
229             Printf("%s\r\n", p);
230             continue;
231         }
232         if (p[0] == '.' && p[1] == '\0') {
233             syslog(L_ERROR, "%s single dot in %s", ClientHost, lp->File);
234             continue;
235         }
236         /* matching patterns against patterns is not that
237            good but it's better than nothing ... */
238         if (lp == &INFOdistribpats) {
239             if (*p == '\0' || *p == '#' || *p == ';' || *p == ' ')
240                 continue;
241             if (PERMspecified) {
242                 if ((q = strchr(p, ':')) == NULL)
243                     continue;
244                 q++;
245                 if ((save = strchr(q, ':')) == NULL)
246                     continue;
247                 *save = '\0';
248                 grplist[0] = q;
249                 if (!PERMmatch(PERMreadlist, grplist))
250                     continue;
251                 *save = ':';
252             }
253             Printf("%s\r\n", p);
254             continue;
255         }
256         if (lp == &INFOdistribs || lp == &INFOmoderators) {
257             if (*p != '\0' && *p != '#' && *p != ';' && *p != ' ')
258                 Printf("%s\r\n", p);
259             continue;
260         }
261         savec = '\0';
262         for (save = p; *save != '\0'; save++) {
263             if (*save == ' ' || *save == '\t') {
264                 savec = *save;
265                 *save = '\0';
266                 break;
267             }
268         }
269               
270         if (PERMspecified) {
271             grplist[0] = p;
272             if (!PERMmatch(PERMreadlist, grplist))
273                 continue;
274         }
275         if (wildarg && !uwildmat(p, wildarg))
276             continue;
277         if (savec != '\0')
278             *save = savec;
279         Printf("%s\r\n", p);
280     }
281     QIOclose(qp);
282
283     Printf(".\r\n");
284 }