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