BSD 4_3_Tahoe release
[unix-history] / usr / src / new / nntp / common / clientlib.c
CommitLineData
ca67e7b4
C
1/*
2 * NNTP client routines.
3 */
4
5#ifndef lint
6static char *sccsid = "@(#)clientlib.c 1.5 (Berkeley) 10/15/87";
7#endif
8
9#include <stdio.h>
10#include <sys/types.h>
11#include <sys/socket.h>
12#include <netinet/in.h>
13#include <netdb.h>
14
15#if defined(AF_DECnet) && defined(ultrix)
16# ifndef DECNET
17# define DECNET
18# endif
19#endif
20
21#ifdef DECNET
22#include <netdnet/dn.h>
23#include <netdnet/dnetdb.h>
24#endif
25
26#include "response_codes.h"
27
28FILE *ser_rd_fp = NULL;
29FILE *ser_wr_fp = NULL;
30
31/*
32 * getserverbyfile Get the name of a server from a named file.
33 * Handle white space and comments.
34 * Use NNTPSERVER environment variable if set.
35 *
36 * Parameters: "file" is the name of the file to read.
37 *
38 * Returns: Pointer to static data area containing the
39 * first non-ws/comment line in the file.
40 * NULL on error (or lack of entry in file).
41 *
42 * Side effects: None.
43 */
44
45char *
46getserverbyfile(file)
47char *file;
48{
49 register FILE *fp;
50 register char *cp;
51 static char buf[256];
52 char *index();
53 char *getenv();
54
55 if (cp = getenv("NNTPSERVER")) {
56 (void) strcpy(buf, cp);
57 return (buf);
58 }
59
60 if (file == NULL)
61 return (NULL);
62
63 fp = fopen(file, "r");
64 if (fp == NULL)
65 return (NULL);
66
67 while (fgets(buf, sizeof (buf), fp) != NULL) {
68 if (*buf == '\n' || *buf == '#')
69 continue;
70 cp = index(buf, '\n');
71 if (cp)
72 *cp = '\0';
73 (void) fclose(fp);
74 return (buf);
75 }
76
77 (void) fclose(fp);
78 return (NULL); /* No entry */
79}
80
81
82/*
83 * server_init Get a connection to the remote news server.
84 *
85 * Parameters: "machine" is the machine to connect to.
86 *
87 * Returns: -1 on error
88 * server's initial response code on success.
89 *
90 * Side effects: Connects to server.
91 * "ser_rd_fp" and "ser_wr_fp" are fp's
92 * for reading and writing to server.
93 */
94
95server_init(machine)
96char *machine;
97{
98 int sockt_rd, sockt_wr;
99 char line[256];
100 char *cp;
101 char *index();
102
103#ifdef DECNET
104 cp = index(machine, ':');
105
106 if (cp && cp[1] == ':') {
107 *cp = '\0';
108 sockt_rd = get_dnet_socket(machine);
109 } else
110 sockt_rd = get_tcp_socket(machine);
111#else
112 sockt_rd = get_tcp_socket(machine);
113#endif
114
115 if (sockt_rd < 0)
116 return (-1);
117
118 /*
119 * Now we'll make file pointers (i.e., buffered I/O) out of
120 * the socket file descriptor. Note that we can't just
121 * open a fp for reading and writing -- we have to open
122 * up two separate fp's, one for reading, one for writing.
123 */
124
125 if ((ser_rd_fp = fdopen(sockt_rd, "r")) == NULL) {
126 perror("server_init: fdopen #1");
127 return (-1);
128 }
129
130 sockt_wr = dup(sockt_rd);
131 if ((ser_wr_fp = fdopen(sockt_wr, "w")) == NULL) {
132 perror("server_init: fdopen #2");
133 ser_rd_fp = NULL; /* from above */
134 return (-1);
135 }
136
137 /* Now get the server's signon message */
138
139 (void) get_server(line, sizeof(line));
140 return (atoi(line));
141}
142
143
144/*
145 * get_tcp_socket -- get us a socket connected to the news server.
146 *
147 * Parameters: "machine" is the machine the server is running on.
148 *
149 * Returns: Socket connected to the news server if
150 * all is ok, else -1 on error.
151 *
152 * Side effects: Connects to server.
153 *
154 * Errors: Printed via perror.
155 */
156
157get_tcp_socket(machine)
158char *machine;
159{
160 int s, x = 0;
161 register char **cp;
162 struct sockaddr_in sin;
163 struct servent *getservbyname(), *sp;
164 struct hostent *gethostbyname(), *hp;
165
166 if ((sp = getservbyname("nntp", "tcp")) == NULL) {
167 fprintf(stderr, "nntp/tcp: Unknown service.\n");
168 return (-1);
169 }
170
171 if ((hp = gethostbyname(machine)) == NULL) {
172 fprintf(stderr, "%s: Unknown host.\n", machine);
173 return (-1);
174 }
175
176 bzero((char *) &sin, sizeof(sin));
177 sin.sin_family = hp->h_addrtype;
178 sin.sin_port = sp->s_port;
179
180 /*
181 * The following is kinda gross. The name server under 4.3
182 * returns a list of addresses, each of which should be tried
183 * in turn if the previous one fails. However, 4.2 hostent
184 * structure doesn't have this list of addresses.
185 * Under 4.3, h_addr is a #define to h_addr_list[0].
186 * We use this to figure out whether to include the NS specific
187 * code...
188 */
189
190#ifdef h_addr
191
192 /* get a socket and initiate connection -- use multiple addresses */
193
194 for (cp = hp->h_addr_list; cp && *cp; cp++) {
195 s = socket(hp->h_addrtype, SOCK_STREAM, 0);
196 if (s < 0) {
197 perror("socket");
198 return (-1);
199 }
200 bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
201
202 if (x < 0)
203 fprintf(stderr, "trying %s\n", inet_ntoa(sin.sin_addr));
204 x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
205 if (x == 0)
206 break;
207 fprintf(stderr, "connection to %s: ", inet_ntoa(sin.sin_addr));
208 perror("");
209 (void) close(s);
210 }
211 if (x < 0) {
212 fprintf(stderr, "giving up...\n");
213 return (-1);
214 }
215#else /* no name server */
216
217 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { /* Get the socket */
218 perror("socket");
219 return (-1);
220 }
221
222 /* And then connect */
223
224 bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
225 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
226 perror("connect");
227 (void) close(s);
228 return (-1);
229 }
230
231#endif
232
233 return (s);
234}
235
236#ifdef DECNET
237/*
238 * get_dnet_socket -- get us a socket connected to the news server.
239 *
240 * Parameters: "machine" is the machine the server is running on.
241 *
242 * Returns: Socket connected to the news server if
243 * all is ok, else -1 on error.
244 *
245 * Side effects: Connects to server.
246 *
247 * Errors: Printed via nerror.
248 */
249
250get_dnet_socket(machine)
251char *machine;
252{
253 int s, area, node;
254 struct sockaddr_dn sdn;
255 struct nodeent *getnodebyname(), *np;
256
257 bzero((char *) &sdn, sizeof(sdn));
258
259 switch (s = sscanf( machine, "%d%*[.]%d", &area, &node )) {
260 case 1:
261 node = area;
262 area = 0;
263 case 2:
264 node += area*1024;
265 sdn.sdn_add.a_len = 2;
266 sdn.sdn_family = AF_DECnet;
267 sdn.sdn_add.a_addr[0] = node % 256;
268 sdn.sdn_add.a_addr[1] = node / 256;
269 break;
270 default:
271 if ((np = getnodebyname(machine)) == NULL) {
272 fprintf(stderr,
273 "%s: Unknown host.\n", machine);
274 return (-1);
275 } else {
276 bcopy(np->n_addr,
277 (char *) sdn.sdn_add.a_addr,
278 np->n_length);
279 sdn.sdn_add.a_len = np->n_length;
280 sdn.sdn_family = np->n_addrtype;
281 }
282 break;
283 }
284 sdn.sdn_objnum = 0;
285 sdn.sdn_flags = 0;
286 sdn.sdn_objnamel = strlen("NNTP");
287 bcopy("NNTP", &sdn.sdn_objname[0], sdn.sdn_objnamel);
288
289 if ((s = socket(AF_DECnet, SOCK_STREAM, 0)) < 0) {
290 nerror("socket");
291 return (-1);
292 }
293
294 /* And then connect */
295
296 if (connect(s, (struct sockaddr *) &sdn, sizeof(sdn)) < 0) {
297 nerror("connect");
298 close(s);
299 return (-1);
300 }
301
302 return (s);
303}
304#endif
305
306
307
308/*
309 * handle_server_response
310 *
311 * Print some informative messages based on the server's initial
312 * response code. This is here so inews, rn, etc. can share
313 * the code.
314 *
315 * Parameters: "response" is the response code which the
316 * server sent us, presumably from "server_init",
317 * above.
318 * "server" is the news server we got the
319 * response code from.
320 *
321 * Returns: -1 if the error is fatal (and we should exit).
322 * 0 otherwise.
323 *
324 * Side effects: None.
325 */
326
327handle_server_response(response, server)
328int response;
329char *server;
330{
331 switch (response) {
332 case OK_NOPOST: /* fall through */
333 printf(
334 "NOTE: This machine does not have permission to post articles.\n");
335 printf(
336 " Please don't waste your time trying.\n\n");
337
338 case OK_CANPOST:
339 return (0);
340 break;
341
342 case ERR_ACCESS:
343 printf(
344 "This machine does not have permission to use the %s news server.\n",
345 server);
346 return (-1);
347 break;
348
349 default:
350 printf("Unexpected response code from %s news server: %d\n",
351 server, response);
352 return (-1);
353 break;
354 }
355 /*NOTREACHED*/
356}
357
358
359/*
360 * put_server -- send a line of text to the server, terminating it
361 * with CR and LF, as per ARPA standard.
362 *
363 * Parameters: "string" is the string to be sent to the
364 * server.
365 *
366 * Returns: Nothing.
367 *
368 * Side effects: Talks to the server.
369 *
370 * Note: This routine flushes the buffer each time
371 * it is called. For large transmissions
372 * (i.e., posting news) don't use it. Instead,
373 * do the fprintf's yourself, and then a final
374 * fflush.
375 */
376
377void
378put_server(string)
379char *string;
380{
381#ifdef DEBUG
382 fprintf(stderr, ">>> %s\n", string);
383#endif
384 fprintf(ser_wr_fp, "%s\r\n", string);
385 (void) fflush(ser_wr_fp);
386}
387
388
389/*
390 * get_server -- get a line of text from the server. Strips
391 * CR's and LF's.
392 *
393 * Parameters: "string" has the buffer space for the
394 * line received.
395 * "size" is the size of the buffer.
396 *
397 * Returns: -1 on error, 0 otherwise.
398 *
399 * Side effects: Talks to server, changes contents of "string".
400 */
401
402get_server(string, size)
403char *string;
404int size;
405{
406 register char *cp;
407 char *index();
408
409 if (fgets(string, size, ser_rd_fp) == NULL)
410 return (-1);
411
412 if ((cp = index(string, '\r')) != NULL)
413 *cp = '\0';
414 else if ((cp = index(string, '\n')) != NULL)
415 *cp = '\0';
416#ifdef DEBUG
417 fprintf(stderr, "<<< %s\n", string);
418#endif
419
420 return (0);
421}
422
423
424/*
425 * close_server -- close the connection to the server, after sending
426 * the "quit" command.
427 *
428 * Parameters: None.
429 *
430 * Returns: Nothing.
431 *
432 * Side effects: Closes the connection with the server.
433 * You can't use "put_server" or "get_server"
434 * after this routine is called.
435 */
436
437void
438close_server()
439{
440 char ser_line[256];
441
442 if (ser_wr_fp == NULL || ser_rd_fp == NULL)
443 return;
444
445 put_server("QUIT");
446 (void) get_server(ser_line, sizeof(ser_line));
447
448 (void) fclose(ser_wr_fp);
449 (void) fclose(ser_rd_fp);
450}