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