1 #include <config.h>
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #ifdef HAVE_UNISTD_H
6 #include <unistd.h>
7 #endif
8 #ifdef HAVE_DIRENT_H
9 # include <dirent.h>
10 #else
11 # define dirent direct
12 # ifdef HAVE_SYS_NDIR_H
13 #  include <sys/ndir.h>
14 # endif
15 # ifdef HAVE_SYS_DIR_H
16 #  include <sys/dir.h>
17 # endif
18 #endif
19 #include <string.h>
20 #include <sys/types.h>
21 #include <netdb.h>
22 #include <sys/stat.h>
23 #include <errno.h>
24 #include <ctype.h>
25 
26 #ifdef DMALLOC
27 #include <dmalloc.h>
28 #endif
29 
30 #include "both.h"
31 #include "suck_config.h"
32 #include "phrases.h"
33 
34 /* set up defaults */
35 char **test_phrases=default_test_phrases;
36 char **both_phrases=default_both_phrases;
37 
38 #define MAXCMDLEN 128	/* length of longest command to send + its args */
39 struct {
40 	const char *command;
41 	int response;
42 } cmds[]  = { { "help", 100 },
43 	      { "list", 215 },
44 	      { "list newsgroups", 215 },
45 	      { "newgroups", 231},
46 	      { "list overview.fmt", 215},
47 };
48 
49 enum { CMD_HELP, CMD_LIST, CMD_DESC, CMD_NEWGRP, CMD_OVERVIEW };
50 enum { RETVAL_OK = 0, RETVAL_ERROR = -1, RETVAL_BAD_DATES = -2, RETVAL_NOAUTH = -3};
51 
52 /*-functions----*/
53 int do_a_command(int, int, FILE *, char *, char *, char *, int, void *);
54 int check_date_format(char *, char *, char *);
55 void load_phrases(const char *);
56 void free_phrases(void);
57 
58 /*------------------------------------------------------------------*/
main(int argc,char * argv[])59 int main(int argc, char *argv[]) {
60 
61 	int sockfd, response, loop, cmd, quiet, mode_reader, do_ssl, retval = RETVAL_OK;
62 	struct stat sbuf;
63 	unsigned short int portnr;
64 	FILE *fptr = stdout;		/* used to print output to */
65 	void *ssl_struct;
66 
67 	char *host, *buf, *ptr, *cmdargs, *userid, *passwd;
68 	char ndate[14];	/* 6 for yymmdd 6 for hhmmss and 1 for space and 1 for null */
69 
70 	const char *phrases;
71 
72 	/* set up defaults */
73 	host = getenv("NNTPSERVER");
74 	cmd = CMD_HELP;
75 	loop = 1;
76 	cmdargs = NULL;
77 	portnr = DEFAULT_NNRP_PORT;
78 	passwd = NULL;
79 	userid = NULL;
80 	phrases = NULL;
81 	mode_reader = FALSE;
82 	do_ssl = FALSE;
83 	quiet = FALSE;		/* do we show status or not */
84 
85 	/* have to do this next so if set on cmd line, overrides this */
86 #ifdef N_PHRASES		/* in case someone nukes def */
87 	if(stat(N_PHRASES, &sbuf) == 0 && S_ISREG(sbuf.st_mode)) {
88 		/* we have a regular phrases file make it the default */
89 		phrases = N_PHRASES;
90 	}
91 
92 #endif
93 
94 	if(argc > 1 && argv[loop][0] != '-') {
95 		host = argv[loop++];
96 	}
97 	for(;loop<argc;loop++) {
98 		if(argv[loop][0] == '-') {
99 			switch(argv[loop][1]) {
100 			  case 'a':
101 				cmd = CMD_LIST;
102 				break;
103 			  case 'd':
104 				  cmd = CMD_DESC;
105 				  break;
106 			  case 'o':
107 				  cmd = CMD_OVERVIEW;
108 				  break;
109 			  case 'q':
110 				  quiet = TRUE;
111 				  break;
112 			  case 'M':
113 				  mode_reader = TRUE;
114 				  break;
115 			  case 'n':
116 				cmd = CMD_NEWGRP;
117 				/* next two args must be date & time YYMMDD HHMMSS format */
118 				if(loop + 2 > argc) {
119 					error_log(ERRLOG_REPORT, test_phrases[0], NULL);
120 					retval = RETVAL_ERROR;
121 				}
122 				else {
123 					if((retval = check_date_format(ndate, argv[loop+1], argv[loop+2])) == RETVAL_OK) {
124 						cmdargs = ndate;
125 					}
126 					else {
127 						error_log(ERRLOG_REPORT, test_phrases[1], argv[loop+1], argv[loop+2], NULL);
128 					}
129 					loop += 2;	/* past the args if there, if not this will finish up the arg loop */
130 				}
131 				break;
132 			  case 'e':	/* use default error log path */
133 				error_log(ERRLOG_SET_FILE, ERROR_LOG, NULL);
134 				break;
135 			  case 'E':	/* error log path */
136 				if(loop+1 == argc) {
137 					error_log(ERRLOG_REPORT, test_phrases[2], NULL);
138 					retval = RETVAL_ERROR;
139 				}
140 				else {
141 					error_log(ERRLOG_SET_FILE, argv[++loop], NULL);
142 				}
143 				break;
144 			  case 's':	/* use default status log path */
145 				if((fptr = fopen(STATUS_LOG, "a")) == NULL) {
146 					MyPerror(test_phrases[3]);
147 					retval = RETVAL_ERROR;
148 				}
149 				break;
150 			  case 'S':	/* status log path */
151 				if(loop+1 == argc) {
152 					error_log(ERRLOG_REPORT, test_phrases[12], NULL);
153 					retval = RETVAL_ERROR;
154 				}
155 				else if((fptr = fopen(argv[++loop], "a")) == NULL) {
156 					MyPerror(test_phrases[3]);
157 					retval = RETVAL_ERROR;
158 				}
159 				break;
160 			  case 'N':	/* override port nr */
161 				if(loop+1 == argc) {
162 					error_log(ERRLOG_REPORT, test_phrases[4], NULL);
163 					retval = RETVAL_ERROR;
164 				}
165 				else {
166 					portnr = atoi(argv[++loop]);
167 				}
168 				break;
169 			  case 'U': 	/* next arg is userid for authorization */
170 				if(loop+1 == argc) {
171 					error_log(ERRLOG_REPORT, test_phrases[5], NULL);
172 					retval = RETVAL_ERROR;
173 				}
174 				else {
175 					userid = argv[++loop];
176 				}
177 				break;
178 			  case 'P': 	/* next arg is password for authorization */
179 				if(loop+1 == argc) {
180 					error_log(ERRLOG_REPORT, test_phrases[6], NULL);
181 					retval = RETVAL_ERROR;
182 				}
183 				else {
184 					passwd = argv[++loop];
185 				}
186 				break;
187 			  case 'Q':   /* get from env */
188 				userid = getenv("NNTP_USER");
189 				passwd = getenv("NNTP_PASS");
190 				break;
191 			  case 'l': 	/* next arg is phrases file */
192 				if(loop+1 == argc) {
193 					error_log(ERRLOG_REPORT, test_phrases[11], NULL);
194 					retval = RETVAL_ERROR;
195 				}
196 				else {
197 					phrases = argv[++loop];
198 				}
199 				break;
200 #ifdef TIMEOUT
201 			case 'T':  /* timeout */
202 				if(loop+1 == argc) {
203 					error_log(ERRLOG_REPORT, test_phrases[13], NULL);
204 					retval = RETVAL_ERROR;
205 				}
206 				else {
207 					TimeOut = atoi(argv[++loop]);
208 				}
209 				break;
210 #endif
211 #ifdef HAVE_LIBSSL
212 			case 'z':
213 				do_ssl = TRUE;
214 				portnr = DEFAULT_SSL_PORT;
215 				break;
216 #endif
217 			  default:
218 				retval = RETVAL_ERROR;
219 				error_log(ERRLOG_REPORT, test_phrases[7], argv[loop], NULL);
220 			}
221 		}
222 		else {
223 			retval = RETVAL_ERROR;
224 			error_log(ERRLOG_REPORT, test_phrases[7], argv[loop], NULL);
225 		}
226 	}
227 
228 	if(retval == RETVAL_OK) {
229 		load_phrases(phrases);	/* this is here so everything displays okay */
230 
231 		sockfd = connect_to_nntphost( host, NULL, 0, (quiet == FALSE)?  fptr : NULL, portnr, do_ssl, &ssl_struct);
232 		if(sockfd < 0 ) {
233 			retval = RETVAL_ERROR;
234 		}
235 		/* get the announcement line */
236 		else if(sgetline(sockfd, &buf, do_ssl, ssl_struct) < 0) {
237 			retval = RETVAL_ERROR;
238 		}
239 		else {
240 			ptr = number(buf, &response);
241 			if(quiet == FALSE) {
242 				fprintf(fptr,"%s",ptr);	/* print it */
243 			}
244 
245 			if(response != 200 && response != 201) {
246 				/* not a valid response */
247 				retval = RETVAL_ERROR;
248 			}
249 			else {
250 				if(mode_reader == TRUE) {
251 					sputline(sockfd, "mode reader\r\n", do_ssl, ssl_struct);
252 					sgetline(sockfd, &buf, do_ssl, ssl_struct);
253 				}
254 				do_a_command(sockfd, cmd, fptr, cmdargs, userid, passwd, do_ssl, ssl_struct);
255 			}
256 		}
257 		if(sockfd >= 0) {
258 			disconnect_from_nntphost(sockfd, do_ssl, &ssl_struct);
259 		}
260 
261 		if(retval == RETVAL_ERROR) {
262 			error_log(ERRLOG_REPORT, test_phrases[8], host, NULL);
263 		}
264 	}
265 	if(fptr != stdout && fptr != NULL) {
266 		fclose(fptr);
267 	}
268 	free_phrases();	/* do this last so everything is displayed correctly */
269 	exit(retval);
270 }
271 /*-------------------------------------------------------------------------------*/
do_a_command(int sockfd,int cmd,FILE * fptr,char * cmdargs,char * userid,char * passwd,int do_ssl,void * ssl_struct)272 int do_a_command(int sockfd, int cmd, FILE *fptr, char *cmdargs, char *userid, char *passwd, int do_ssl, void *ssl_struct ) {
273 	char *ptr, *buf, cmdstr[MAXCMDLEN], buf2[MAXCMDLEN];
274 	int response;
275 	int len, done, retval = RETVAL_OK;
276 
277 	/* build command to send */
278 	strcpy(cmdstr, cmds[cmd].command);
279 	if(cmdargs != NULL) {
280 		strcat(cmdstr, " ");
281 		strcat(cmdstr, cmdargs);
282 	}
283 	strcat(cmdstr, "\r\n");
284 
285 	sputline(sockfd, cmdstr, do_ssl, ssl_struct);		/* which command do I run? */
286 	sgetline(sockfd, &buf, do_ssl, ssl_struct);			/* get response */
287 
288 	ptr = number(buf, &response);
289 	if(response == 480 ) { /* we must do authorization */
290 		sprintf(buf2, "AUTHINFO USER %s\r\n", userid);
291 		sputline(sockfd, buf2, do_ssl, ssl_struct);
292 		len = sgetline(sockfd, &buf, do_ssl, ssl_struct);
293 		if( len < 0) {
294 			retval = RETVAL_ERROR;
295 		}
296 		else {
297 			ptr  = number(buf, &response);
298 			if(response != 381) {
299 				error_log(ERRLOG_REPORT, test_phrases[9], buf, NULL);
300 				retval = RETVAL_NOAUTH;
301 			}
302 			else {
303 				sprintf(buf2, "AUTHINFO PASS %s\r\n", passwd);
304 				sputline(sockfd, buf2, do_ssl, ssl_struct);
305 				len = sgetline(sockfd, &buf, do_ssl, ssl_struct);
306 				if(len < 0) {
307 					retval = RETVAL_ERROR;
308 				}
309 				else {
310 					number(buf, &response);
311 					switch(response) {
312 					  case 281: /* bingo resend original command*/
313 						sputline(sockfd, cmdstr, do_ssl, ssl_struct);
314 						len = sgetline(sockfd, &buf, do_ssl, ssl_struct);
315 						if( len < 0) {
316 							retval = RETVAL_ERROR;
317 						}
318 						else {
319 							ptr = number(buf,&response);
320 						}
321 						break;
322 					  case 502: /* permission denied */
323 						retval = RETVAL_NOAUTH;
324 						error_log(ERRLOG_REPORT, test_phrases[10], NULL);
325 						break;
326 					  default: /* wacko error */
327 						error_log(ERRLOG_REPORT, test_phrases[9], ptr, NULL);
328 						retval = RETVAL_NOAUTH;
329 						break;
330 					}
331 				}
332 			}
333 		}
334 	}
335 	if(cmds[cmd].response != response) {
336 		error_log(ERRLOG_REPORT, "%v1%\n", buf, NULL);
337 		retval = RETVAL_ERROR;
338 	}
339 	else {
340 		/* okay here we go */
341 		fprintf(fptr, "%s", ptr);
342 		/* commands are ended by a . on a line by itself */
343 
344 		len = done = 0;
345 		while( len >=0 && done == 0) {
346 
347 			len = sgetline(sockfd, &buf, do_ssl, ssl_struct);
348 
349 			if(len == 2 && strcmp(buf, ".\n") == 0) {
350 				done = 1;
351 			}
352 			else if(len > 0) {
353 				fprintf(fptr,"%s",buf);
354 			}
355 		}
356 		if(len < 0) {
357 			retval = RETVAL_ERROR;
358 		}
359 	}
360 	return retval;
361 }
362 /*---------------------------------------------------------------*/
check_date_format(char * dates,char * indate,char * intime)363 int check_date_format(char *dates, char *indate, char *intime) {
364 
365 	/* if indate & intime are not valid format, return error */
366 	/* when done, dates will be yymmdd hhmmss */
367 
368 	int i, retval = RETVAL_OK;
369 
370 	/* now test my incoming args */
371 	if(indate == NULL || intime == NULL) {
372 		retval = RETVAL_ERROR;
373 	}
374 	else if(strlen(indate) != 6 || strlen(intime) != 6) {
375 		retval = RETVAL_ERROR;
376 	}
377 	else {
378 		for(i=0;i<6;i++) {
379 			if(!isdigit(indate[i]) || !isdigit(intime[i])) {
380 					retval = RETVAL_ERROR;
381 			}
382 		}
383 	}
384 
385 	if(retval == RETVAL_OK) {
386 		sprintf(dates, "%s %s", indate, intime);
387 	}
388 	return retval;
389 }
390 /*--------------------------------------------------------------------------------*/
391 /* THE strings in this routine is the only one not in the arrays, since           */
392 /* we are in the middle of reading the arrays, and they may or may not be valid.  */
393 /*--------------------------------------------------------------------------------*/
load_phrases(const char * phrases)394 void load_phrases(const char *phrases) {
395 
396 	int error=TRUE;
397 	FILE *fpi;
398 	char buf[MAXLINLEN];
399 
400 	if(phrases != NULL) {
401 
402 		if((fpi = fopen(phrases, "r")) == NULL) {
403 			MyPerror(phrases);
404 		}
405 		else {
406 			fgets(buf, MAXLINLEN, fpi);
407 			if(strncmp( buf, SUCK_VERSION, strlen(SUCK_VERSION)) != 0) {
408 				error_log(ERRLOG_REPORT, "Invalid Phrase File, wrong version\n", NULL);
409 			}
410 			else if((both_phrases = read_array(fpi, NR_BOTH_PHRASES, TRUE)) != NULL) {
411 				read_array(fpi, NR_RPOST_PHRASES, FALSE);	/* skip these */
412 				if((test_phrases = read_array(fpi, NR_TEST_PHRASES, TRUE)) != NULL) {
413 					error = FALSE;
414 				}
415 			}
416 		}
417 		fclose(fpi);
418 		if(error == TRUE) {
419 			/* reset back to default */
420 			error_log(ERRLOG_REPORT, "Using default Language phrases\n", NULL);
421 			test_phrases = default_test_phrases;
422 			both_phrases = default_both_phrases;
423 		}
424 	}
425 }
426 /*--------------------------------------------------------------------------------*/
free_phrases(void)427 void free_phrases(void) {
428 		/* free up the memory alloced in load_phrases() */
429 		if(test_phrases != default_test_phrases) {
430 			free_array(NR_TEST_PHRASES, test_phrases);
431 		}
432 		if(both_phrases != default_both_phrases) {
433 			free_array(NR_BOTH_PHRASES, both_phrases);
434 		}
435 
436 }
437