1 /*  $Id: getlist.c 10014 2016-05-05 12:42:31Z iulius $
2 **
3 **  Send a LIST command to an NNTP server and print the results.
4 */
5 
6 #include "config.h"
7 #include "clibrary.h"
8 #include <errno.h>
9 
10 #include "inn/innconf.h"
11 #include "inn/messages.h"
12 #include "inn/nntp.h"
13 #include "inn/qio.h"
14 #include "inn/vector.h"
15 #include "inn/libinn.h"
16 #include "inn/paths.h"
17 
18 static const char usage[] = "\
19 Usage: getlist [-AR] [-h host] [-p port] [list [pattern [types]]]\n\
20 \n\
21 getlist obtains a list from an NNTP server and prints it out.  By default,\n\
22 the active file is retrieved, but any list that the NNTP server supports\n\
23 can be requested.  The pattern is passed to the remote server as a filter\n\
24 on the returned values.  If the list parameter is \"active\", a third\n\
25 parameter can be given, listing the acceptable group types.  Only groups of\n\
26 that type (y and m being the most common) are returned.\n";
27 
28 
29 /*
30 **  Print out an appropriate error message for a bad NNTP response code and
31 **  exit.
32 */
33 static void
die_nntp_code(enum nntp_code code,const char * line)34 die_nntp_code(enum nntp_code code, const char *line)
35 {
36     if (code == 0)
37         die("unexpected server response: %s", line);
38     else
39         die("unexpected server response: %03d %s", code, line);
40 }
41 
42 
43 /*
44 **  Print out an appropriate error message for a bad NNTP status and exit.
45 */
46 static void
die_nntp_status(enum nntp_status status)47 die_nntp_status(enum nntp_status status)
48 {
49     switch (status) {
50     case NNTP_READ_LONG:
51         die("line from server too long for 128KB buffer");
52     case NNTP_READ_EOF:
53         die("server closed connection unexpectedly");
54     case NNTP_READ_TIMEOUT:
55         die("read from server timed out");
56     default:
57         sysdie("cannot read response from server");
58     }
59 }
60 
61 
62 /*
63 **  Send a command and pattern to the remote server and make sure we get an
64 **  appropriate response.
65 */
66 static void
send_list(struct nntp * nntp,const char * command,const char * pattern)67 send_list(struct nntp *nntp, const char *command, const char *pattern)
68 {
69     bool okay;
70     enum nntp_status status;
71     enum nntp_code code;
72     char *line;
73 
74     if (pattern != NULL)
75         okay = nntp_send_line(nntp, "LIST %s %s", command, pattern);
76     else
77         okay = nntp_send_line(nntp, "LIST %s", command);
78     if (!okay)
79         sysdie("cannot send LIST command to server");
80     status = nntp_read_response(nntp, &code, &line);
81     if (status != NNTP_READ_OK)
82         die_nntp_status(status);
83     if (code != NNTP_OK_LIST)
84         die_nntp_code(code, line);
85 }
86 
87 
88 /*
89 **  Print out the results of a LIST command.  Used for generic list commands
90 **  or active commands without a type parameter.
91 */
92 static void
print_list(struct nntp * nntp)93 print_list(struct nntp *nntp)
94 {
95     enum nntp_status status;
96     char *line;
97 
98     status = nntp_read_line(nntp, &line);
99     while (status == NNTP_READ_OK) {
100         if (strcmp(line, ".") == 0)
101             break;
102         printf("%s\n", line);
103         status = nntp_read_line(nntp, &line);
104     }
105     if (status != NNTP_READ_OK)
106         die_nntp_status(status);
107 }
108 
109 
110 /*
111 **  Print out the results of a LIST ACTIVE command, limited to particular
112 **  group types.
113 */
114 static void
print_active(struct nntp * nntp,const char * types)115 print_active(struct nntp *nntp, const char *types)
116 {
117     enum nntp_status status;
118     char *line;
119     struct cvector *group = NULL;
120 
121     status = nntp_read_line(nntp, &line);
122     while (status == NNTP_READ_OK) {
123         if (strcmp(line, ".") == 0)
124             break;
125         group = cvector_split_space(line, group);
126         if (group->count != 4) {
127             warn("malformed line from server: %s", line);
128             continue;
129         }
130         if (strchr(types, group->strings[3][0]) != NULL)
131             printf("%s %s %s %s\n", group->strings[0], group->strings[1],
132                    group->strings[2], group->strings[3]);
133         status = nntp_read_line(nntp, &line);
134     }
135     if (status != NNTP_READ_OK)
136         die_nntp_status(status);
137 }
138 
139 
140 /*
141 **  Get the username and password for a remote site from the local password
142 **  file.  username and password will be set to newly allocated strings on
143 **  success.
144 */
145 static bool
get_authinfo(const char * server,char ** username,char ** password)146 get_authinfo(const char *server, char **username, char **password)
147 {
148     char *path, *line;
149     QIOSTATE *passwords;
150     struct cvector *info = NULL;
151 
152     path = concatpath(innconf->pathetc, INN_PATH_NNTPPASS);
153     passwords = QIOopen(path);
154     if (passwords == NULL) {
155         if (errno != ENOENT)
156             warn("cannot open %s", path);
157         return false;
158     }
159     free(path);
160     while ((line = QIOread(passwords)) != NULL) {
161         if (line[0] == '\0' || line[0] == '#')
162             continue;
163         info = cvector_split(line, ':', info);
164         if (info->count > 4 || info->count < 3)
165             continue;
166         if (info->count == 4 && strcmp(info->strings[3], "authinfo") != 0)
167             continue;
168         if (strcasecmp(info->strings[0], server) != 0)
169             continue;
170         *username = xstrdup(info->strings[1]);
171         *password = xstrdup(info->strings[2]);
172         return true;
173     }
174     return false;
175 }
176 
177 
178 /*
179 **  Send AUTHINFO information to a remote site.  Returns true if successful,
180 **  false on failure.  Problems sending to the remote site (as opposed to just
181 **  having the remote site reject the authentication) are fatal.
182 */
183 static bool
send_authinfo(struct nntp * nntp,const char * username,const char * password)184 send_authinfo(struct nntp *nntp, const char *username, const char *password)
185 {
186     enum nntp_status status;
187     enum nntp_code code;
188     char *line;
189 
190     if (!nntp_send_line(nntp, "AUTHINFO USER %s", username))
191         sysdie("cannot send AUTHINFO USER to remote server");
192     status = nntp_read_response(nntp, &code, &line);
193     if (status != NNTP_READ_OK)
194         die_nntp_status(status);
195     if (code == NNTP_OK_AUTHINFO)
196         return true;
197     if (code != NNTP_CONT_AUTHINFO)
198         return false;
199     if (!nntp_send_line(nntp, "AUTHINFO PASS %s", password))
200         sysdie("cannot send AUTHINFO PASS to remote server");
201     status = nntp_read_response(nntp, &code, &line);
202     if (status != NNTP_READ_OK)
203         die_nntp_status(status);
204     return (code == NNTP_OK_AUTHINFO);
205 }
206 
207 
208 int
main(int argc,char * argv[])209 main(int argc, char *argv[])
210 {
211     struct nntp *nntp;
212     const char *host = NULL;
213     const char *list = "active";
214     const char *pattern = NULL;
215     const char *types = NULL;
216     enum nntp_status status;
217     enum nntp_code response;
218     char *line;
219     unsigned short port = NNTP_PORT;
220     bool authinfo = false;
221     bool reader = false;
222     int option;
223 
224     message_program_name = "getlist";
225     if (!innconf_read(NULL))
226         exit(1);
227     host = innconf->server;
228 
229     /* Parse options. */
230     while ((option = getopt(argc, argv, "Ah:p:R")) != EOF) {
231         switch (option) {
232         case 'A':
233             authinfo = true;
234             break;
235         case 'h':
236             host = optarg;
237             break;
238         case 'p':
239             port = atoi(optarg);
240             if (port == 0) {
241                 die("%s is not a valid port number", optarg);
242             }
243             break;
244         case 'R':
245             reader = true;
246             break;
247         default:
248             die("%s", usage);
249         }
250     }
251     argc -= optind;
252     argv += optind;
253 
254     /* Read optional arguments. */
255     if (argc > 3)
256         die("too many arguments");
257     if (argc >= 1)
258         list = argv[0];
259     if (argc >= 2)
260         pattern = argv[1];
261     if (argc == 3)
262         types = argv[2];
263     if (strcasecmp(list, "active") != 0 && types != NULL)
264         die("group types can only be specified with a list type of active");
265 
266     /* Connect to the server. */
267     if (host == NULL)
268         sysdie("cannot get server name");
269     nntp = nntp_connect(host, port, 128 * 1024, DEFAULT_TIMEOUT);
270     if (nntp == NULL)
271         sysdie("cannot connect to server %s:%hu", host, port);
272     status = nntp_read_response(nntp, &response, &line);
273     if (status != NNTP_READ_OK)
274         die_nntp_status(status);
275     if (response < 200 || response > 201)
276         die_nntp_code(response, line);
277 
278     /* Switch to nnrpd if desired. */
279     if (reader) {
280         if (!nntp_send_line(nntp, "MODE READER"))
281             sysdie("cannot send MODE READER command to server %s", host);
282         status = nntp_read_response(nntp, &response, &line);
283         if (status != NNTP_READ_OK)
284             die_nntp_status(status);
285     }
286 
287     /* Authenticate if desired. */
288     if (authinfo) {
289         char *username, *password;
290 
291         if (get_authinfo(host, &username, &password)) {
292             if (!send_authinfo(nntp, username, password))
293                 warn("server %s did not accept authentication", host);
294             free(username);
295             free(password);
296         } else
297             warn("no authentication information found for %s", host);
298     }
299 
300     /* Get and display the data. */
301     send_list(nntp, list, pattern);
302     if (types != NULL)
303         print_active(nntp, types);
304     else
305         print_list(nntp);
306 
307     /* Be polite and say goodbye; it gives the server a chance to shut the
308        connection down cleanly. */
309     if (nntp_send_line(nntp, "QUIT"))
310         nntp_read_response(nntp, &response, &line);
311     nntp_free(nntp);
312     exit(0);
313 }
314