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