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