1 /* nntpclient.c
2 */
3 /* This software is copyrighted as detailed in the LICENSE file. */
4 
5 
6 #include "EXTERN.h"
7 #include "common.h"
8 
9 #ifdef SUPPORT_NNTP
10 
11 #include "nntpinit.h"
12 #include "INTERN.h"
13 #include "nntpclient.h"
14 
15 time_t last_command_diff;
16 
17 char* savestr _((char*));
18 #ifndef USE_DEBUGGING_MALLOC
19 char* safemalloc _((MEM_SIZE));
20 #endif
21 #ifdef NNTP_HANDLE_TIMEOUT
22 int nntp_handle_timeout _((void));
23 #endif
24 
25 int nntp_handle_nested_lists _((void));
26 
27 #ifdef NNTP_HANDLE_AUTH_ERR
28 int nntp_handle_auth_err _((void));
29 #endif
30 
31 int
nntp_connect(machine,verbose)32 nntp_connect(machine,verbose)
33 char* machine;
34 bool_int verbose;
35 {
36     int response;
37 
38     if (nntplink.rd_fp)
39 	return 1;
40 
41     if (nntplink.flags & NNTP_FORCE_AUTH_NEEDED)
42 	nntplink.flags |= NNTP_FORCE_AUTH_NOW;
43     nntplink.flags |= NNTP_NEW_CMD_OK;
44 #if 0
45   try_to_connect:
46 #endif
47     if (verbose) {
48 	printf("Connecting to %s...",machine);
49 	fflush(stdout);
50     }
51     switch (response = server_init(machine)) {
52     case NNTP_GOODBYE_VAL:
53 	if (atoi(ser_line) == response) {
54 	    char tmpbuf[LBUFLEN];
55 	    if (verbose)
56 		printf("failed: %s\n",&ser_line[4]);
57 	    else {
58 		sprintf(tmpbuf,"News server \"%s\" is unavailable: %s\n",
59 			machine,&ser_line[4]);
60 		nntp_init_error(tmpbuf);
61 	    }
62 	    response = 0;
63 	    break;
64 	}
65     case -1:
66 	if (verbose)
67 	    printf("failed.\n");
68 	else {
69 	    sprintf(ser_line,"News server \"%s\" is unavailable.\n",machine);
70 	    nntp_init_error(ser_line);
71 	}
72 	response = 0;
73 	break;
74     case NNTP_ACCESS_VAL:
75 	if (verbose)
76 	    printf("access denied.\n");
77 	else {
78 	    sprintf(ser_line,
79 		    "This machine does not have permission to use the %s news server.\n\n",
80 		    machine);
81 	    nntp_init_error(ser_line);
82 	}
83 	response = -1;
84 	break;
85     case NNTP_NOPOSTOK_VAL:
86 	if (verbose)
87 	    printf("Done (but no posting).\n\n");
88 	response = 1;
89 	break;
90     case NNTP_POSTOK_VAL:
91 	if (verbose)
92 	    printf("Done.\n") FLUSH;
93 	response = 1;
94 	break;
95     default:
96 	if (verbose)
97 	    printf("unknown response: %d.\n",response);
98 	else {
99 	    sprintf(ser_line,"\nUnknown response code %d from %s.\n",
100 		    response,machine);
101 	    nntp_init_error(ser_line);
102 	}
103 	response = 0;
104 	break;
105     }
106 #if 0
107     if (!response) {
108 	if (handle_no_connect() > 0)
109 	    goto try_to_connect;
110     }
111 #endif
112     return response;
113 }
114 
115 char*
nntp_servername(name)116 nntp_servername(name)
117 char* name;
118 {
119     FILE* fp;
120 
121     if (FILE_REF(name) && (fp = fopen(name, "r")) != NULL) {
122 	while (fgets(ser_line, sizeof ser_line, fp) != NULL) {
123 	    if (*ser_line == '\n' || *ser_line == '#')
124 		continue;
125 	    if ((name = index(ser_line, '\n')) != NULL)
126 		*name = '\0';
127 	    name = ser_line;
128 	    break;
129 	}
130 	fclose(fp);
131     }
132     return name;
133 }
134 
135 int
nntp_command(bp)136 nntp_command(bp)
137 char* bp;
138 {
139     time_t now;
140 #if defined(DEBUG) && defined(FLUSH)
141     if (debug & DEB_NNTP)
142 	printf(">%s\n", bp) FLUSH;
143 #endif
144 #if defined(NNTP_HANDLE_TIMEOUT) || defined(NNTP_HANDLE_AUTH_ERR)
145     strcpy(last_command, bp);
146 # ifdef NNTP_HANDLE_TIMEOUT
147     if (!nntplink.rd_fp)
148 	return nntp_handle_timeout();
149 # endif
150 # ifdef NNTP_HANDLE_AUTH_ERR
151     if (nntplink.flags & NNTP_FORCE_AUTH_NOW) {
152 	nntplink.flags &= ~NNTP_FORCE_AUTH_NOW;
153 	return nntp_handle_auth_err();
154     }
155 # endif
156 #endif
157     if (!(nntplink.flags & NNTP_NEW_CMD_OK)) {
158 	int ret = nntp_handle_nested_lists();
159 	if (ret <= 0)
160 	    return ret;
161     }
162     if (fprintf(nntplink.wr_fp, "%s\r\n", bp) < 0
163      || fflush(nntplink.wr_fp) < 0)
164 #ifdef NNTP_HANDLE_TIMEOUT
165 	return nntp_handle_timeout();
166 #else
167 	return -2;
168 #endif
169     now = time((time_t*)NULL);
170     last_command_diff = now - nntplink.last_command;
171     nntplink.last_command = now;
172     return 1;
173 }
174 
175 int
nntp_check()176 nntp_check()
177 {
178     int ret;
179     int len = 0;
180 
181  read_it:
182 #ifdef HAS_SIGHOLD
183     sighold(SIGINT);
184 #endif
185     errno = 0;
186     ret = (fgets(ser_line, sizeof ser_line, nntplink.rd_fp) == NULL)? -2 : 0;
187 #ifdef HAS_SIGHOLD
188     sigrelse(SIGINT);
189 #endif
190     if (ret < 0) {
191 	if (errno == EINTR)
192 	    goto read_it;
193 	strcpy(ser_line, "503 Server closed connection.");
194     }
195 #ifdef NNTP_HANDLE_TIMEOUT
196     if (len == 0 && atoi(ser_line) == NNTP_TMPERR_VAL
197      && nntp_allow_timeout && last_command_diff > 60) {
198 	ret = nntp_handle_timeout();
199 	switch (ret) {
200 	case 1:
201 	    len = 1;
202 	    goto read_it;
203 	case 0:		/* We're quitting, so pretend it's OK */
204 	    strcpy(ser_line, "205 Ok");
205 	    break;
206 	default:
207 	    break;
208 	}
209     }
210     else
211 #endif
212     if (*ser_line <= NNTP_CLASS_CONT && *ser_line >= NNTP_CLASS_INF)
213 	ret = 1;			/* (this includes NNTP_CLASS_OK) */
214     else if (*ser_line == NNTP_CLASS_FATAL)
215 	ret = -1;
216     /* Even though the following check doesn't catch all possible lists, the
217      * bit will get set right when the caller checks nntp_at_list_end(). */
218     if (atoi(ser_line) == NNTP_LIST_FOLLOWS_VAL)
219 	nntplink.flags &= ~NNTP_NEW_CMD_OK;
220     else
221 	nntplink.flags |= NNTP_NEW_CMD_OK;
222     len = strlen(ser_line);
223     if (len >= 2 && ser_line[len-1] == '\n' && ser_line[len-2] == '\r')
224 	ser_line[len-2] = '\0';
225 #if defined(DEBUG) && defined(FLUSH)
226     if (debug & DEB_NNTP)
227 	printf("<%s\n", ser_line) FLUSH;
228 #endif
229 #ifdef NNTP_HANDLE_AUTH_ERR
230     if (atoi(ser_line) == NNTP_AUTH_NEEDED_VAL) {
231 	ret = nntp_handle_auth_err();
232 	if (ret > 0)
233 	    goto read_it;
234     }
235 #endif
236     return ret;
237 }
238 
239 bool
nntp_at_list_end(s)240 nntp_at_list_end(s)
241 char* s;
242 {
243     if (!s || (*s == '.' && (s[1] == '\0' || s[1] == '\r'))) {
244 	nntplink.flags |= NNTP_NEW_CMD_OK;
245 	return 1;
246     }
247     nntplink.flags &= ~NNTP_NEW_CMD_OK;
248     return 0;
249 }
250 
251 /* This returns 1 when it reads a full line, 0 if it reads a partial
252  * line, and -2 on EOF/error.  The maximum length includes a spot for
253  * the null-terminator, and we need room for our "\r\n"-stripping code
254  * to work right, so "len" MUST be at least 3.
255  */
256 int
nntp_gets(bp,len)257 nntp_gets(bp, len)
258 char* bp;
259 int  len;
260 {
261     int ch, n = 0;
262     char* cp = bp;
263 
264 #ifdef HAS_SIGHOLD
265     sighold(SIGINT);
266 #endif
267     if (nntplink.trailing_CR) {
268 	*cp++ = '\r';
269 	len--;
270 	nntplink.trailing_CR = 0;
271     }
272     while (1) {
273 	if (len == 1) {
274 	    if (cp[-1] == '\r') {
275 		/* Hold a trailing CR until next time because we may need
276 		 * to strip it if it is followed by a newline. */
277 		cp--;
278 		nntplink.trailing_CR = 1;
279 	    }
280 	    break;
281 	}
282 	do {
283 	    errno = 0;
284 	    ch = fgetc(nntplink.rd_fp);
285 	} while (errno == EINTR);
286 	if (ch == EOF) {
287 	    nntplink.flags |= NNTP_NEW_CMD_OK;
288 	    n = -2;
289 	    break;
290 	}
291 	if (ch == '\n') {
292 	    if (cp != bp && cp[-1] == '\r')
293 		cp--;
294 	    n = 1;
295 	    break;
296 	}
297 	*cp++ = ch;
298 	len--;
299     }
300     *cp = '\0';
301 #ifdef HAS_SIGHOLD
302     sigrelse(SIGINT);
303 #endif
304     return n;
305 }
306 
307 void
nntp_close(send_quit)308 nntp_close(send_quit)
309 bool_int send_quit;
310 {
311     if (send_quit && nntplink.wr_fp != NULL && nntplink.rd_fp != NULL) {
312 	if (nntp_command("QUIT") > 0)
313 	    nntp_check();
314     }
315     /* the nntp_check() above might have closed these already. */
316     if (nntplink.wr_fp != NULL) {
317 	fclose(nntplink.wr_fp);
318 	nntplink.wr_fp = NULL;
319     }
320     if (nntplink.rd_fp != NULL) {
321 	fclose(nntplink.rd_fp);
322 	nntplink.rd_fp = NULL;
323     }
324     nntplink.flags |= NNTP_NEW_CMD_OK;
325 }
326 
327 #endif /* SUPPORT_NNTP */
328