1 /*
2  *  S-News version 0.1.9 - A Simple News Server
3  *  Copyright (C) 1998 Christopher John Purnell
4  *                     cjp@lost.org.uk
5  *  XPAT support added in 2002 by Tony Houghton <tony@realh.co.uk>
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/utsname.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 #ifdef HAVE_FCNTL_H
32 #include <fcntl.h>
33 #endif
34 #ifdef HAVE_SYSLOG_H
35 #include <syslog.h>
36 #endif
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #ifdef HAVE_MALLOC_H
41 #include <malloc.h>
42 #endif
43 #ifdef STDC_HEADERS
44 #include <string.h>
45 #include <stdlib.h>
46 #endif
47 #include <signal.h>
48 #include <stdio.h>
49 #include <netinet/in.h>
50 #include <netdb.h>
51 #include <ctype.h>
52 #if defined(USER)
53 #include <pwd.h>
54 #elif defined(GROUP)
55 #include <grp.h>
56 #endif
57 #include <gdbm.h>
58 
59 #include "snews.h"
60 
61 struct active
62 {
63 	struct active *next;
64 	char *name;
65 	unsigned last;
66 	unsigned first;
67 	char status;
68 };
69 
70 struct overview
71 {
72 	struct overview *next;
73 	unsigned num;
74 	char *subject;
75 	char *from;
76 	char *date;
77 	char *msgid;
78 	char *references;
79 	unsigned bytes;
80 	unsigned lines;
81 	char *xref;
82 };
83 
84 static void set_ug_id(void);
85 static int get_fqdn(void);
86 static void host_access(char *);
87 static int read_active(char *);
88 static void get_name(struct sockaddr_in *);
89 static int match_pat(char *,char *);
90 static GDBM_FILE open_history(char *,int);
91 static void make_name(unsigned);
92 static void read_overview(char *);
93 static void free_overview(struct overview *);
94 static struct overview *find_overview(unsigned);
95 static char *read_str(FILE *,int *);
96 static unsigned read_num(FILE *,int *);
97 static char *read_eol(FILE *,int *);
98 static int read_field(FILE *,char *,unsigned);
99 static int read_hline(FILE *,char *,unsigned,struct overview *);
100 static int read_body(FILE *,struct overview *);
101 static char *make_str(unsigned char *);
102 static char *fix_str(char *,int *);
103 
104 static void do_article(char *,char);
105 static void do_group(char *);
106 static void do_help(void);
107 static void do_next(char);
108 static void do_list(char *);
109 static void do_listgroup(char *);
110 static void do_xover(char *);
111 static void do_xhdr(char *);
112 static void do_xpat(char *);
113 static void do_xpath(char *);
114 static void do_ihave(char *);
115 static void do_post(char *);
116 
117 static int check_read(void);
118 static int check_post(void);
119 static int check_xfer(void);
120 static void show_article(char *,char *,unsigned,char);
121 static struct active *find_group(char *);
122 static void list_file(char *,char *);
123 static void send_article(char);
124 static FILE *open_pipe(char *,char *,char *);
125 static int close_pipe(FILE *);
126 
127 static char *progname;
128 static char fqdn[BUFLEN];
129 static char peername[BUFLEN];
130 static char groupdir[BUFLEN];
131 static char canread=0,canpost=0,canxfer=0;
132 static struct active *act=0;
133 static struct active *grp=0;
134 static unsigned art=0;
135 static struct overview *ovr=0;
136 static char *dirend;
137 
main(int argc,char ** argv)138 int main(int argc, char **argv)
139 {
140 	unsigned char line[BUFLEN];
141 
142 	set_ug_id();
143 
144 	signal(SIGHUP,SIG_IGN);
145 
146 	progname = argv[0];
147 
148 	openlog("nntpd", LOG_PID|LOG_CONS, LOG_NEWS);
149 
150 	if (get_fqdn())
151 	{
152 		printf("400 Who am I?\r\n");
153 		fflush(stdout);
154 		closelog();
155 		return (1);
156 	}
157 
158 	host_access(CONFDIR"/access");
159 
160 	syslog(LOG_INFO,"connect %s:%s%s%s",peername,
161 	       canread?" read":"",canpost?" post":"",canxfer?" xfer":"");
162 
163 	if (!canread && !canpost && !canxfer)
164 	{
165 		printf("502 access restricted\r\n");
166 		fflush(stdout);
167 		closelog();
168 		return (1);
169 	}
170 
171 	if (read_active(CONFDIR"/active"))
172 	{
173 		printf("400 no active file\r\n");
174 		fflush(stdout);
175 		closelog();
176 		return (1);
177 	}
178 
179 	if (chdir(SPOOLDIR))
180 	{
181 		syslog(LOG_ERR,"%s: %m",SPOOLDIR);
182 		printf("400 no news spool\r\n");
183 		fflush(stdout);
184 		closelog();
185 		return (1);
186 	}
187 
188 	printf("%u %s NNTP server - %s\r\n",canpost?200:201,fqdn,
189 	       canpost?"posting ok":"no posting");
190 	fflush(stdout);
191 
192 	while (fgets(line,BUFLEN,stdin))
193 	{
194 		unsigned char c,*cp=line,*arg;
195 
196 		while ((c=*(cp)) && !isspace(c)) cp++;
197 		if (cp == line) continue;
198 		*(arg=cp) = '\0';
199 		if (c)
200 		{
201 			while ((c=*(++arg)) && isspace(c));
202 			cp = arg + strlen(arg);
203 			while ((cp>arg) && isspace(*(--cp))) *cp = '\0';
204 		}
205 
206 		if (!strcasecmp(line,"article"))
207 		{
208 			do_article(arg,'0');
209 		}
210 		else if (!strcasecmp(line,"head"))
211 		{
212 			do_article(arg,'1');
213 		}
214 		else if (!strcasecmp(line,"body"))
215 		{
216 			do_article(arg,'2');
217 		}
218 		else if (!strcasecmp(line,"stat"))
219 		{
220 			do_article(arg,'3');
221 		}
222 		else if (!strcasecmp(line,"group"))
223 		{
224 			do_group(arg);
225 		}
226 		else if (!strcasecmp(line,"list"))
227 		{
228 			do_list(arg);
229 		}
230 		else if (!strcasecmp(line,"listgroup"))
231 		{
232 			do_listgroup(arg);
233 		}
234 		else if (!strcasecmp(line,"next"))
235 		{
236 			do_next(0);
237 		}
238 		else if (!strcasecmp(line,"last"))
239 		{
240 			do_next(1);
241 		}
242 		else if (!strcasecmp(line,"xhdr"))
243 		{
244 			do_xhdr(arg);
245 		}
246 		else if (!strcasecmp(line,"xpat"))
247 		{
248 			do_xpat(arg);
249 		}
250 		else if (!strcasecmp(line,"xover"))
251 		{
252 			do_xover(arg);
253 		}
254 		else if (!strcasecmp(line,"xpath"))
255 		{
256 			do_xpath(arg);
257 		}
258 		else if (!strcasecmp(line,"post"))
259 		{
260 			do_post(arg);
261 		}
262 		else if (!strcasecmp(line,"ihave"))
263 		{
264 			do_ihave(arg);
265 		}
266 		else if (!strcasecmp(line,"help"))
267 		{
268 			do_help();
269 		}
270 		else if (!strcasecmp(line,"quit"))
271 		{
272 			break;
273 		}
274 		else
275 		{
276 			printf("500 command not recognized\r\n");
277 		}
278 
279 		fflush(stdout);
280 	}
281 	if (ferror(stdin))
282 		syslog(LOG_ERR,"%s: %m",progname);
283 
284 	printf("205 closing connection - goodbye!\r\n");
285 	fflush(stdout);
286 	closelog();
287 
288 	return (0);
289 }
290 
set_ug_id(void)291 static void set_ug_id(void)
292 {
293 #if defined(USER)
294 	struct passwd *pwd;
295 
296 	if ((pwd = getpwnam(USER)))
297 	{
298 		setregid(pwd->pw_gid,pwd->pw_gid);
299 		setreuid(pwd->pw_uid,pwd->pw_uid);
300 	}
301 #elif defined(GROUP)
302 	struct group *grp;
303 
304 	if ((grp = getgrnam(GROUP)))
305 	{
306 		setregid(grp->gr_gid,grp->gr_gid);
307 	}
308 #endif
309 #if defined(UMASK)
310 	umask(UMASK);
311 #endif
312 }
313 
get_fqdn(void)314 static int get_fqdn(void)
315 {
316 	struct utsname buf;
317 	struct hostent *hp;
318 
319 	if (uname(&buf) || !(hp = gethostbyname(buf.nodename)))
320 	{
321 		syslog(LOG_ERR,"%s: %m",progname);
322 		return (1);
323 	}
324 
325 	strcpy(fqdn,hp->h_name);
326 	return (0);
327 }
328 
host_access(char * file)329 static void host_access(char *file)
330 {
331 	char line[BUFLEN];
332 	struct sockaddr_in sin;
333 	int len = sizeof(sin);
334 	FILE *fp;
335 	char *cp;
336 
337 	if (getpeername(0,(struct sockaddr *)&sin,&len))
338 		strcpy(peername,"stdin");
339 	else
340 		get_name(&sin);
341 
342 	if (!(fp = fopen(file,"r")))
343 	{
344 		syslog(LOG_ERR,"%s: %m",file);
345 		return;
346 	}
347 
348 	while(fgets(line,BUFLEN,fp))
349 	{
350 		if (!(cp = strrchr(line,'\n')))
351 		{
352 			syslog(LOG_ERR,"%s: long line",file);
353 			break;
354 		}
355 		*cp = '\0';
356 		if ((cp = strchr(line,'#')))
357 			*cp = '\0';
358 		if (!*line) continue;
359 		if ((cp = strchr(line,'\t')) <= line)
360 		{
361 			syslog(LOG_ERR,"%s: malformed line",file);
362 			break;
363 		}
364 		*(cp++) = '\0';
365 
366 		if (match_pat(line,peername))
367 		{
368 			if (strchr(cp,'r')) canread=-1;
369 			if (strchr(cp,'p')) canpost=-1;
370 			if (strchr(cp,'x')) canxfer=-1;
371 		}
372 	}
373 
374 	fclose(fp);
375 }
376 
get_name(struct sockaddr_in * sin)377 static void get_name(struct sockaddr_in *sin)
378 {
379 	unsigned char *ptr = (unsigned char *)&sin->sin_addr;
380 	char name[BUFLEN];
381 	struct hostent *hp;
382 
383 	sprintf(peername,"%u.%u.%u.%u",ptr[0],ptr[1],ptr[2],ptr[3]);
384 
385 	if (!(hp = gethostbyaddr(ptr,sizeof(sin->sin_addr),sin->sin_family)))
386 		return;
387 
388 	if (strlen(hp->h_name) >= BUFLEN)
389 		return;
390 
391 	strcpy(name, hp->h_name);
392 
393 	if (!(hp = gethostbyname(name)))
394 		return;
395 
396 	if (((struct in_addr *)hp->h_addr)->s_addr != sin->sin_addr.s_addr)
397 		return;
398 
399 	if (strlen(hp->h_name) >= BUFLEN)
400 		return;
401 
402 	strcpy(peername,hp->h_name);
403 }
404 
read_active(char * file)405 static int read_active(char *file)
406 {
407 	char line[BUFLEN];
408 	FILE *fp;
409 	struct active **ptr=&act;
410 	int ret=0;
411 
412 	if (!(fp = fopen(file,"r")))
413 	{
414 		syslog(LOG_ERR,"%s: %m",file);
415 		return (1);
416 	}
417 
418 	while(fgets(line,BUFLEN,fp))
419 	{
420 		struct active *ap;
421 		char *cp;
422 		unsigned n;
423 
424 		if (!strchr(line,'\n'))
425 		{
426 			syslog(LOG_ERR,"%s: long line",file);
427 			ret = 1;
428 			break;
429 		}
430 		if ((cp = strchr(line,' ')) <= line)
431 		{
432 			syslog(LOG_ERR,"%s: malformed line",file);
433 			ret = 1;
434 			break;
435 		}
436 		*(cp++)='\0';
437 		if (!((ap = malloc(sizeof(*ap))) &&
438 		      (ap->name = strdup(line))))
439 		{
440 			syslog(LOG_ERR,"%s: %m",file);
441 			ret = 1;
442 			break;
443 		}
444 		ap->last = strtoul(cp,&cp,0);
445 		ap->first = strtoul(cp,&cp,0);
446 		if ((n = strspn(cp," ")) == 0 ||
447 		    (ap->status = cp[n]) == '\n')
448 		{
449 			syslog(LOG_ERR,"%s: malformed line",file);
450 			ret = 1;
451 			break;
452 		}
453 
454 		*ptr = ap; ptr = &ap->next;
455 		ap->next = 0;
456 	}
457 	if (ferror(fp))
458 	{
459 		syslog(LOG_ERR,"%s: %m",file);
460 		ret = 1;
461 	}
462 	fclose(fp);
463 
464 	return (ret);
465 }
466 
do_article(char * arg,char cmd)467 static void do_article(char *arg,char cmd)
468 {
469 	char *cp;
470 	unsigned u;
471 
472 	if (check_read()) return;
473 
474 	u = strtoul(arg,&cp,10);
475 
476 	if (!*cp)
477 	{
478 		struct overview *o;
479 
480 		if (!grp)
481 		{
482 			printf("412 no newsgroup has been selected\r\n");
483 			return;
484 		}
485 
486 		if (!*arg && !(u = art))
487 		{
488 			printf("420 no article has been selected\r\n");
489 			return;
490 		}
491 
492 		if (!(o = find_overview(u)))
493 		{
494 
495 			printf("423 no such article number in this group\r\n");
496 			return;
497 		}
498 
499 		make_name(u);
500 		show_article(groupdir,o->msgid,u,cmd);
501 	}
502 	else
503 	{
504 		GDBM_FILE dbf;
505 		datum key,val;
506 		char *str;
507 
508 		if (!(dbf = open_history(CONFDIR"/history",GDBM_READER)))
509 		{
510 			printf("503 program error\r\n");
511 			return;
512 		}
513 
514 		key.dsize = strlen(key.dptr = arg) + 1;
515 
516 		val = gdbm_fetch(dbf,key);
517 
518 		if ((str = val.dptr))
519 		{
520 			cp = strrchr(str,'/');
521 			u = strtoul(cp?cp:str,0,10);
522 			show_article(str,arg,u,cmd);
523 			free(str);
524 		}
525 		else
526 		{
527 			printf("430 no such article\r\n");
528 		}
529 
530 		gdbm_close(dbf);
531 	}
532 }
533 
show_article(char * path,char * msgid,unsigned n,char cmd)534 static void show_article(char *path,char *msgid,unsigned n,char cmd)
535 {
536 	FILE *fp;
537 	int a,c;
538 
539 	if (cmd == '3')
540 	{
541 		printf("223 %u %s article retrieved - request text separately\r\n",n,msgid);
542 		return;
543 	}
544 
545 	if (!(fp = fopen(path,"r")))
546 	{
547 		printf("430 no such article\r\n");
548 		return;
549 	}
550 
551 	printf("22%c %u %s article retrieved - text follows\r\n",cmd,n,msgid);
552 
553 	a = '\n';
554 
555 	if (cmd == '1')
556 	{
557 		while ((c = fgetc(fp)) != EOF)
558 		{
559 			if (c == '\n')
560 			{
561 				if (a == '\n')
562 					break;
563 				fputc('\r',stdout);
564 			}
565 			else if (c == '.' && a == '\n')
566 			{
567 				fputc('.',stdout);
568 			}
569 			fputc(a=c,stdout);
570 		}
571 	}
572 	else
573 	{
574 		if (cmd == '2')
575 		{
576 			while ((c = fgetc(fp)) != EOF)
577 			{
578 				if (c == '\n' && a == '\n')
579 					break;
580 				a = c;
581 			}
582 		}
583 
584 		while ((c = fgetc(fp)) != EOF)
585 		{
586 			if (c == '\n') fputc('\r',stdout);
587 			else if (c == '.' && a == '\n') fputc('.',stdout);
588 			fputc(a=c,stdout);
589 		}
590 	}
591 
592 	if (a != '\n')
593 	{
594 		fputc('\r',stdout);
595 		fputc('\n',stdout);
596 	}
597 
598 
599 	printf(".\r\n");
600 
601 	fclose(fp);
602 }
603 
do_group(char * arg)604 static void do_group(char *arg)
605 {
606 	struct active *ap;
607 
608 	if (check_read()) return;
609 
610 	if ((ap = find_group(arg)))
611 	{
612 		printf("211 %u %u %u %s group selected\r\n",
613 		       (ap->last-grp->first)+1,ap->first,ap->last,ap->name);
614 	}
615 }
616 
find_group(char * name)617 static struct active *find_group(char *name)
618 {
619 	struct active *ap = act;
620 
621 	while (ap)
622 	{
623 		if (!strcasecmp(ap->name,name))
624 			break;
625 		ap = ap->next;
626 	}
627 
628 	if (!ap)
629 	{
630 		printf("411 no such news group\r\n");
631 	}
632 	else
633 	{
634 		char c,*s,*cp;
635 
636 		art = (grp=ap)->first;
637 		s = ap->name;
638 		cp = groupdir;
639 
640 		while ((c=*(s++)))
641 		{
642 			if (c=='.') c='/';
643 			*(cp++) = c;
644 		}
645 		*(cp++) = '/';
646 		strcpy(dirend=cp,".overview");
647 
648 		read_overview(groupdir);
649 	}
650 
651 	return (ap);
652 }
653 
do_next(char cmd)654 static void do_next(char cmd)
655 {
656 	struct active *ap;
657 	unsigned u;
658 
659 	if (check_read()) return;
660 
661 	if (!(ap=grp))
662 	{
663 		printf("412 no newsgroup selected\r\n");
664 		return;
665 	}
666 
667 	if (!(u=art))
668 	{
669 		printf("420 no article has been selected\r\n");
670 		return;
671 	}
672 
673 	if (cmd)
674 	{
675 		while (--u >= ap->first)
676 		{
677 			struct overview *o;
678 
679 			if ((o = find_overview(u)))
680 			{
681 				printf("223 %u %s article retrieved - request text separately\r\n",art=u,o->msgid);
682 				return;
683 			}
684 		}
685 
686 		printf("422 no previous article in this group\r\n");
687 	}
688 	else
689 	{
690 		while (++u <= ap->last)
691 		{
692 			struct overview *o;
693 
694 			if ((o = find_overview(u)))
695 			{
696 				printf("223 %u %s article retrieved - request text separately\r\n",art=u,o->msgid);
697 				return;
698 			}
699 		}
700 
701 		printf("421 no next article in this group\r\n");
702 	}
703 }
704 
do_list(char * arg)705 static void do_list(char *arg)
706 {
707 	unsigned char c,*cp=arg;
708 
709 	if (check_read()) return;
710 
711 	while ((c=*cp) && !isspace(c)) ++cp;
712 	while ((c=*cp) && isspace(c)) *(cp++) = '\0';
713 
714 	if (!*cp) cp="*";
715 
716 	if (!*arg || !strcasecmp(arg,"active"))
717 	{
718 		list_file(CONFDIR"/active",cp);
719 	}
720 	else if (!strcasecmp(arg,"newsgroups"))
721 	{
722 		list_file(CONFDIR"/newsgroups",cp);
723 	}
724 	else if (!strcasecmp(arg,"overview.fmt"))
725 	{
726 		printf("215 information follows\r\n");
727 		printf("Subject:\r\nFrom:\r\nDate:\r\nMessage-ID:\r\nReferences:\r\nBytes:\r\nLines:\r\nXref:full\r\n");
728 		printf(".\r\n");
729 	}
730 	else
731 	{
732 		printf("500 command not recognized\r\n");
733 	}
734 }
735 
list_file(char * file,char * pat)736 static void list_file(char *file,char *pat)
737 {
738 	char line[BUFLEN];
739 	FILE *fp;
740 	int c;
741 	unsigned u=0;
742 
743 	if (!(fp = fopen(file,"r")))
744 	{
745 		syslog(LOG_ERR,"%s: %m",file);
746 		printf("503 program error\r\n");
747 		return;
748 	}
749 
750 	printf("215 information follows\r\n");
751 
752 	while((c=fgetc(fp)) != EOF)
753 	{
754 		if (!isspace(c))
755 		{
756 			if (u < BUFLEN) line[u++] = c;
757 			continue;
758 		}
759 		else
760 		{
761 			if (u < BUFLEN)
762 			{
763 				line[u] = '\0';
764 				if (match_pat(pat,line))
765 				{
766 					fwrite(line,u,1,stdout);
767 
768 					do
769 					{
770 						if (c=='\n') break;
771 						fputc(c,stdout);
772 					}
773 					while((c=fgetc(fp)) != EOF);
774 
775 					fputc('\r',stdout);
776 					fputc(c='\n',stdout);
777 				}
778 			}
779 
780 			while (c!='\n' && (c=fgetc(fp))!=EOF);
781 
782 			u = 0;
783 		}
784 	}
785 
786 	fclose(fp);
787 
788 	printf(".\r\n");
789 }
790 
do_listgroup(char * arg)791 static void do_listgroup(char *arg)
792 {
793 	unsigned u;
794 
795 	if (check_read()) return;
796 
797 	if (!*arg)
798 	{
799 		if (!grp)
800 		{
801 			printf("412 no newsgroup has been selected\r\n");
802 			return;
803 		}
804 	}
805 	else
806 	{
807 		if (!find_group(arg))
808 			return;
809 	}
810 
811 	printf("211 list of article numbers follow\r\n");
812 
813 	for (u=grp->first; u<=grp->last; u++)
814 	{
815 		if (find_overview(u))
816 			printf("%u\r\n",u);
817 	}
818 
819 	printf(".\r\n");
820 }
821 
822 static char *xhdr_h[7] = { "Subject","From","Date","Message-ID",
823 		      "References","Bytes","Lines" };
824 
do_xhdr(char * arg)825 static void do_xhdr(char *arg)
826 {
827 	char *fmt,*cp;
828 	unsigned char c;
829 	int i;
830 	unsigned u,l;
831 
832 	if (check_read()) return;
833 
834 	if (!grp)
835 	{
836 		printf("412 no newsgroup has been selected\r\n");
837 		return;
838 	}
839 
840 	cp = arg;
841 	while ((c=*(cp)) && !isspace(c)) cp++;
842 	if (*cp) *(cp++) = '\0';
843 
844 	i = -1; fmt = "%u \r\n";
845 	for (u=0; u<7; ++u)
846 	{
847 		if (!strcasecmp(arg,xhdr_h[u]))
848 		{
849 			fmt = ((i=u) < 5) ? "%u %s\r\n" : "%u %u\r\n";
850 		}
851 	}
852 
853 	while ((c=*(cp)) && isspace(c)) cp++;
854 	if (*cp)
855 	{
856 		u = strtoul(cp,&cp,10);
857 		if (*cp == '-')
858 		{
859 			if (*(++cp)) l = strtoul(cp,&cp,10);
860 			else l = grp->last;
861 		}
862 		else l = u;
863 	}
864 	else u = l = art;
865 
866 	if (!u)
867 	{
868 		printf("420 no article has been selected\r\n");
869 		return;
870 	}
871 
872 	printf("221 Header follows\r\n");
873 
874 	while (u <= l)
875 	{
876 		struct overview *o;
877 
878 		if ((o = find_overview(u)))
879 		{
880 			printf(fmt,u,(&o->subject)[i]);
881 		}
882 		++u;
883 	}
884 
885 	printf(".\r\n");
886 }
887 
do_xover(char * arg)888 static void do_xover(char *arg)
889 {
890 	char *cp;
891 	unsigned u,l;
892 
893 	if (check_read()) return;
894 
895 	if (!grp)
896 	{
897 		printf("412 no newsgroup has been selected\r\n");
898 		return;
899 	}
900 
901 	if (*arg)
902 	{
903 		u = strtoul(arg,&cp,10);
904 		if (*cp == '-')
905 		{
906 			if (*(++cp)) l = strtoul(cp,&cp,10);
907 			else l = grp->last;
908 		}
909 		else l = u;
910 	}
911 	else u = l = art;
912 
913 	if (!u)
914 	{
915 		printf("420 no article has been selected\r\n");
916 		return;
917 	}
918 
919 	printf("224 Overview information follows\r\n");
920 
921 	while (u <= l)
922 	{
923 		struct overview *o;
924 
925 		if ((o = find_overview(u)))
926 		{
927 			printf("%u\t%s\t%s\t%s\t%s\t%s\t%u\t%u\t%s\r\n",
928 			       o->num,
929 			       o->subject,
930 			       o->from,
931 			       o->date,
932 			       o->msgid,
933 			       o->references,
934 			       o->bytes,
935 			       o->lines,
936 			       o->xref);
937 		}
938 		++u;
939 	}
940 
941 	printf(".\r\n");
942 }
943 
do_xpath(char * arg)944 static void do_xpath(char *arg)
945 {
946 	GDBM_FILE dbf;
947 	datum key,val;
948 	char *str;
949 
950 	if (check_read()) return;
951 
952 	if (!(dbf = open_history(CONFDIR"/history",GDBM_READER)))
953 	{
954 		printf("503 program error\r\n");
955 		return;
956 	}
957 
958 	key.dsize = strlen(key.dptr = arg) + 1;
959 
960 	val = gdbm_fetch(dbf,key);
961 
962 	if ((str = val.dptr))
963 	{
964 		printf("223 %s\r\n",str);
965 		free(str);
966 	}
967 	else
968 	{
969 		printf("430 no such article\r\n");
970 	}
971 
972 	gdbm_close(dbf);
973 }
974 
do_ihave(char * arg)975 static void do_ihave(char *arg)
976 {
977 	GDBM_FILE dbf;
978 	datum key;
979 	int x;
980 
981 	if (check_xfer()) return;
982 
983 	if (!(dbf = open_history(CONFDIR"/history",GDBM_READER)))
984 	{
985 		printf("503 program error\r\n");
986 		return;
987 	}
988 
989 	key.dsize = strlen(key.dptr = arg) + 1;
990 
991 	x = gdbm_exists(dbf,key);
992 
993 	gdbm_close(dbf);
994 
995 	if (x)
996 		printf("435 article not wanted - do not send it\r\n");
997 	else
998 		send_article(1);
999 }
1000 
do_post(char * arg)1001 static void do_post(char *arg)
1002 {
1003 	if (check_post()) return;
1004 
1005 	send_article(0);
1006 }
1007 
send_article(char xfer)1008 static void send_article(char xfer)
1009 {
1010 	FILE *fp;
1011 	int c,a;
1012 
1013 	if (!(fp = open_pipe(xfer?RNEWSPATH:INEWSPATH,
1014 			     xfer?RNEWSARG0:INEWSARG0,
1015 			     xfer?0:"-h")))
1016 	{
1017 		printf("503 program error\r\n");
1018 		return;
1019 	}
1020 
1021 	printf(xfer?"335 send article to be transferred.  End with <CR-LF>.<CR-LF>\r\n":
1022 	       "340 send article to be posted.  End with <CR-LF>.<CR-LF>\r\n");
1023 	fflush(stdout);
1024 
1025 	a = '\n';
1026 	while ((c = fgetc(stdin)) != EOF)
1027 	{
1028 		if (c == '\r') continue;
1029 		if (c == '\n' && a == EOF) break;
1030 		if (c == '.' && a == '\n') a = EOF;
1031 		else fputc(a=c,fp);
1032 	}
1033 
1034 	if (close_pipe(fp))
1035 	{
1036 		printf(xfer?"436 transfer failed - try again later\r\n":
1037 		       "441 posting failed\r\n");
1038 	}
1039 	else
1040 	{
1041 		printf(xfer?"235 article transferred ok\r\n":
1042 		       "240 article posted ok\r\n");
1043 	}
1044 }
1045 
open_pipe(char * path,char * arg0,char * arg1)1046 static FILE *open_pipe(char *path,char *arg0,char *arg1)
1047 {
1048 	int fd,fds[2];
1049 	pid_t pid;
1050 	FILE *fp;
1051 
1052 	if (pipe(fds))
1053 		return (0);
1054 
1055 	if ((pid = fork()) == -1)
1056 	{
1057 		close(fds[0]);
1058 		close(fds[1]);
1059 
1060 		return (0);
1061 	}
1062 
1063 	if (!pid)
1064 	{
1065 		close(fds[1]);
1066 		if ((fd = fds[0]) != 0)
1067 		{
1068 			dup2(fd,0);
1069 			close(fd);
1070 		}
1071 		if ((fd = open("/dev/null",O_WRONLY)) != 1)
1072 		{
1073 			dup2(fd,1);
1074 			close(fd);
1075 		}
1076 		dup2(1,2);
1077 
1078 		execl(path,arg0,arg1,0);
1079 		syslog(LOG_ERR,"%s: %m",path);
1080 		exit(1);
1081 	}
1082 
1083 	close(fds[0]);
1084 
1085 	if (!(fp = fdopen(fds[1],"w")))
1086 		close(fds[1]);
1087 
1088 	return (fp);
1089 }
1090 
close_pipe(FILE * fp)1091 static int close_pipe(FILE *fp)
1092 {
1093 	int status;
1094 
1095 	if (!(status = fclose(fp)))
1096 		wait(&status);
1097 
1098 	return (status);
1099 }
1100 
check_read(void)1101 static int check_read(void)
1102 {
1103 	if (canread) return (0);
1104 	printf("502 read permission denied\r\n");
1105 	return (1);
1106 }
1107 
check_post(void)1108 static int check_post(void)
1109 {
1110 	if (canpost) return (0);
1111 	printf("440 posting not allowed\r\n");
1112 	return (1);
1113 }
1114 
check_xfer(void)1115 static int check_xfer(void)
1116 {
1117 	if (canxfer) return (0);
1118 	printf("480 transfer permission denied\r\n");
1119 	return (1);
1120 }
1121 
do_help(void)1122 static void do_help(void)
1123 {
1124 	printf("100 help text follows\r\n");
1125 	if (canread)
1126 	{
1127 		printf("ARTICLE   HEAD      BODY      STAT\r\n");
1128 		printf("GROUP     LIST      NEXT      LAST\r\n");
1129 		printf("LISTGROUP XHDR      XOVER     XPATH\r\n");
1130 		printf("XPAT      ");
1131 	}
1132 	if (canpost)
1133 	{
1134 		printf("POST      ");
1135 	}
1136 	if (canxfer)
1137 	{
1138 		printf("IHAVE     ");
1139 	}
1140 	printf("\r\nHELP      QUIT\r\n.\r\n");
1141 }
1142 
match_pat(char * pat,char * str)1143 static int match_pat(char *pat,char *str)
1144 {
1145 	unsigned char c,u;
1146 
1147 	while ((c = *(pat++)))
1148 	{
1149 		switch (c)
1150 		{
1151 		case '*':
1152 			while (*str)
1153 			{
1154 				if (match_pat(pat,str))
1155 					return (1);
1156 				++str;
1157 			}
1158 			break;
1159 		case '?':
1160 			if (!*(str++))
1161 				return (0);
1162 			break;
1163 		case '[':
1164 			if ((u=*str))
1165 			{
1166 				unsigned char rev,mat=0,l=0,h;
1167 
1168 				if ((rev = (*pat=='^'?0:1)))
1169 					pat++;
1170 				if ((c = *pat)) do
1171 				{
1172 					pat++;
1173 					if (c=='-' && l && (h=*pat) && h!=']')
1174 					{
1175 						pat++;
1176 						if (u>=l && u<=h) mat=1;
1177 						l = 0;
1178 					}
1179 					else
1180 					{
1181 						if (u==c) mat=1;
1182 						l = c;
1183 					}
1184 				}
1185 				while ((c=*pat) && c!=']');
1186 
1187 				if (mat==rev)
1188 					return (0);
1189 			}
1190 			break;
1191 		default:
1192 			if (*(str++) != c)
1193 				return (0);
1194 			break;
1195 		}
1196 	}
1197 
1198 	return (!*str);
1199 }
1200 
open_history(char * file,int mode)1201 static GDBM_FILE open_history(char *file,int mode)
1202 {
1203 	GDBM_FILE dbf;
1204 
1205 	while (!(dbf = gdbm_open(file,1024,mode,0666,0)))
1206 	{
1207 		if (gdbm_errno != GDBM_CANT_BE_READER &&
1208 		    gdbm_errno != GDBM_CANT_BE_WRITER)
1209 		{
1210 			syslog(LOG_ERR,"%s: %s\n",file,
1211 			       gdbm_strerror(gdbm_errno));
1212 			return (0);
1213 		}
1214 		sleep (1);
1215 	}
1216 	return (dbf);
1217 }
1218 
make_name(unsigned u)1219 void make_name(unsigned u)
1220 {
1221 	char num[12];
1222 	char *cp;
1223 
1224 	*(cp = num+11)='\0';
1225 	do { *(--cp)='0'+u%10; }
1226 	while (u/=10);
1227 
1228 	strcpy(dirend,cp);
1229 }
1230 
read_overview(char * file)1231 static void read_overview(char *file)
1232 {
1233 	FILE *fp;
1234 	struct overview **ptr=&ovr;
1235 	int c,err=0;
1236 
1237 	free_overview(ovr);
1238 	ovr = 0;
1239 
1240 	if (!(fp = fopen(file,"r")))
1241 		return;
1242 
1243 	while((c = fgetc(fp)) != EOF)
1244 	{
1245 		struct overview *o;
1246 
1247 		ungetc(c,fp);
1248 
1249 		if ((o = malloc(sizeof(*ovr))))
1250 		{
1251 			*ptr = o; ptr = &o->next;
1252 			o->next = 0;
1253 
1254 			o->num = read_num(fp,&err);
1255 			o->subject = read_str(fp,&err);
1256 			o->from = read_str(fp,&err);
1257 			o->date = read_str(fp,&err);
1258 			o->msgid = read_str(fp,&err);
1259 			o->references = read_str(fp,&err);
1260 			o->bytes = read_num(fp,&err);
1261 			o->lines = read_num(fp,&err);
1262 			o->xref = read_eol(fp,&err);
1263 
1264 			if (!err) continue;
1265 		}
1266 
1267 		free_overview(ovr);
1268 		ovr = 0;
1269 		break;
1270 	}
1271 
1272 	fclose(fp);
1273 }
1274 
read_str(FILE * fp,int * ptr)1275 static char *read_str(FILE *fp,int *ptr)
1276 {
1277 	char buf[BUFLEN];
1278 	char *str;
1279 
1280 	if (!*ptr)
1281 	{
1282 		if (!read_field(fp,buf,BUFLEN))
1283 		{
1284 			if ((str = strdup(buf)))
1285 				return (str);
1286 		}
1287 		*ptr=1;
1288 	}
1289 
1290 	return (0);
1291 }
1292 
read_num(FILE * fp,int * ptr)1293 static unsigned read_num(FILE *fp,int *ptr)
1294 {
1295 	char buf[BUFLEN];
1296 	char *str;
1297 	unsigned ret;
1298 
1299 	if (!*ptr)
1300 	{
1301 		if (!read_field(fp,buf,BUFLEN))
1302 		{
1303 			ret = strtol(buf,&str,0);
1304 			if (!*str) return (ret);
1305 		}
1306 		*ptr=1;
1307 	}
1308 
1309 	return (0);
1310 }
1311 
read_eol(FILE * fp,int * ptr)1312 static char *read_eol(FILE *fp,int *ptr)
1313 {
1314 	char buf[BUFLEN];
1315 	char *str;
1316 
1317 	if (!*ptr)
1318 	{
1319 		if (read_field(fp,buf,BUFLEN) > 0)
1320 		{
1321 			if ((str = strdup(buf)))
1322 				return (str);
1323 		}
1324 		*ptr=1;
1325 	}
1326 
1327 	return (0);
1328 }
1329 
read_field(FILE * fp,char * str,unsigned len)1330 static int read_field(FILE *fp,char *str,unsigned len)
1331 {
1332 	int c;
1333 	unsigned i=0;
1334 
1335 	while ((c = fgetc(fp)) != '\n' && c != '\t')
1336 	{
1337 		if (c == EOF) return (-1);
1338 		str[i]=c;
1339 		if (++i>=len) return (-1);
1340 	}
1341 	str[i]='\0';
1342 
1343 	return ((c == '\t') ? 0 : 1);
1344 }
1345 
free_overview(struct overview * op)1346 static void free_overview(struct overview *op)
1347 {
1348 	struct overview *o;
1349 
1350 	while ((o = op))
1351 	{
1352 		op = o->next;
1353 
1354 		if (o->subject) free(o->subject);
1355 		if (o->from) free(o->from);
1356 		if (o->date) free(o->date);
1357 		if (o->msgid) free(o->msgid);
1358 		if (o->references) free(o->references);
1359 		if (o->xref) free(o->xref);
1360 		free(o);
1361 	}
1362 }
1363 
find_overview(unsigned n)1364 static struct overview *find_overview(unsigned n)
1365 {
1366 	char line[BUFLEN];
1367 	FILE *fp;
1368 	struct overview *o,**ptr=&ovr;
1369 
1370 	while ((o = *ptr))
1371 	{
1372 		if (o->num == n)
1373 			return (o);
1374 		ptr = &o->next;
1375 	}
1376 
1377 	make_name(n);
1378 
1379 	if ((fp = fopen(groupdir,"r")))
1380 	{
1381 		if ((o = calloc(1,sizeof(*o))))
1382 		{
1383 			int i;
1384 
1385 			o->num = n;
1386 
1387 			while (!(i = read_hline(fp,line,BUFLEN,o)))
1388 			{
1389 				if (!(o->subject ||
1390 				    strncasecmp(line,"Subject:",8)))
1391 				{
1392 					if (!(o->subject = make_str(line+8)))
1393 					{
1394 						i = -1;
1395 						break;
1396 					}
1397 				}
1398 
1399 				if (!(o->from ||
1400 				    strncasecmp(line,"From:",5)))
1401 				{
1402 					if (!(o->from = make_str(line+5)))
1403 					{
1404 						i = -1;
1405 						break;
1406 					}
1407 				}
1408 
1409 				if (!(o->date ||
1410 				    strncasecmp(line,"Date:",5)))
1411 				{
1412 					if (!(o->date = make_str(line+5)))
1413 					{
1414 						i = -1;
1415 						break;
1416 					}
1417 				}
1418 
1419 				if (!(o->msgid ||
1420 				    strncasecmp(line,"Message-ID:",11)))
1421 				{
1422 					if (!(o->msgid = make_str(line+11)))
1423 					{
1424 						i = -1;
1425 						break;
1426 					}
1427 				}
1428 
1429 				if (!(o->references ||
1430 				    strncasecmp(line,"References:",11)))
1431 				{
1432 					if (!(o->references = make_str(line+11)))
1433 					{
1434 						i = -1;
1435 						break;
1436 					}
1437 				}
1438 
1439 				if (!(o->xref ||
1440 				    strncasecmp(line,"Xref:",5)))
1441 				{
1442 					if (!(o->xref = make_str(line)))
1443 					{
1444 						i = -1;
1445 						break;
1446 					}
1447 				}
1448 			}
1449 
1450 			o->subject = fix_str(o->subject,&i);
1451 			o->from = fix_str(o->from,&i);
1452 			o->date = fix_str(o->date,&i);
1453 			o->msgid = fix_str(o->msgid,&i);
1454 			o->references = fix_str(o->references,&i);
1455 			o->xref = fix_str(o->xref,&i);
1456 
1457 			if (i < 0 || read_body(fp,o))
1458 			{
1459 				free_overview(o);
1460 				o = 0;
1461 			}
1462 		}
1463 
1464 		fclose(fp);
1465 	}
1466 
1467 	return (*ptr=o);
1468 }
1469 
1470 
read_hline(FILE * fp,char * buf,unsigned len,struct overview * ovr)1471 int read_hline(FILE *fp,char *buf,unsigned len,struct overview *ovr)
1472 {
1473 	int c;
1474 	unsigned pos=0;
1475 	int ret;
1476 
1477 	if ((c = fgetc(fp)) != '\n')
1478 	{
1479 		ret = -1;
1480 
1481 		while (c != EOF)
1482 		{
1483 			ovr->bytes += 1;
1484 
1485 			if (isspace(c)) c = ' ';
1486 			if (pos+1 < len) buf[pos++] = c;
1487 
1488 			if ((c = fgetc(fp)) == '\n')
1489 			{
1490 				ovr->bytes += 2;
1491 
1492 				if ((c = fgetc(fp)) != EOF &&
1493 				    (!isspace(c) || c == '\n'))
1494 				{
1495 					ungetc(c,fp);
1496 					ret = 0;
1497 					break;
1498 				}
1499 			}
1500 		}
1501 	}
1502 	else
1503 	{
1504 		ovr->bytes += 2;
1505 		ret = 1;
1506 	}
1507 
1508 	buf[pos] = '\0';
1509 	return (ret);
1510 }
1511 
read_body(FILE * fp,struct overview * ovr)1512 int read_body(FILE *fp,struct overview *ovr)
1513 {
1514 	int c;
1515 
1516 	while ((c = fgetc(fp)) != EOF)
1517 	{
1518 		if (c == '\n')
1519 		{
1520 			ovr->lines += 1;
1521 			ovr->bytes += 1;
1522 		}
1523 		ovr->bytes += 1;
1524 	}
1525 
1526 	return (ferror(fp));
1527 }
1528 
make_str(unsigned char * str)1529 char *make_str(unsigned char *str)
1530 {
1531         while(isspace(*str)) ++str;
1532 
1533         return strdup(str);
1534 }
1535 
fix_str(char * str,int * ptr)1536 char *fix_str(char *str,int *ptr)
1537 {
1538 	if (!str)
1539 	{
1540 		if (!(str = strdup("")))
1541 			*ptr = -1;
1542 	}
1543 
1544 	return str;
1545 }
1546 
match_pattern(const char * pat,const char * str)1547 static int match_pattern(const char *pat,const char *str)
1548 {
1549 	char c;
1550 
1551 	while ((c = *(pat++)))
1552 	{
1553 		switch (c)
1554 		{
1555 		case '*':
1556 			if (!*pat)
1557 				return (1);
1558 			while (*str)
1559 			{
1560 				if (match_pattern(pat,str))
1561 					return (1);
1562 				++str;
1563 			}
1564 			break;
1565 		case '?':
1566 			if (!*str)
1567 				return (0);
1568 			break;
1569 		case '[':
1570 			if (*str)
1571 			{
1572 				unsigned char rev,mat=0,l=0,h,u=*str;
1573 
1574 				if ((rev = (*pat=='^'?1:0)))
1575 					pat++;
1576 				if ((c=*pat)) do
1577 				{
1578 					pat++;
1579 					if (c=='-' && l && (h=*pat) && h!=']')
1580 					{
1581 						pat++;
1582 						if (u>=l && u<=h) mat=1;
1583 						l = 0;
1584 					}
1585 					else
1586 					{
1587 						if (u==c) mat=1;
1588 						l = c;
1589 					}
1590 				}
1591 				while ((c=*pat) && c!=']');
1592 				if (c)
1593 					++pat;
1594 				if (mat==rev)
1595 					return (0);
1596 			}
1597 			else
1598 				return (0);
1599 			break;
1600 		default:
1601 			if (*str!=c)
1602 				return (0);
1603 			break;
1604 		}
1605 		++str;
1606 	}
1607 
1608 	return (!*str);
1609 }
1610 
do_xpat(char * arg)1611 static void do_xpat(char *arg)
1612 {
1613 	char *cp;
1614 	unsigned char c;
1615 	int i;
1616 	unsigned u,l;
1617 	char const **patterns = NULL;
1618 	size_t pat_size = 0;
1619 	size_t npats = 0;
1620 
1621 	if (check_read()) return;
1622 
1623 	if (!grp)
1624 	{
1625 		printf("412 no newsgroup has been selected\r\n");
1626 		return;
1627 	}
1628 
1629 	cp = arg;
1630 	while ((c=*(cp)) && !isspace(c)) cp++;
1631 	if (*cp) *(cp++) = '\0';
1632 
1633 	i = -1;
1634 	for (u=0; u<7; ++u)
1635 	{
1636 		if (!strcasecmp(arg,xhdr_h[u]))
1637 		{
1638 		//	fmt = ((i=u) < 5) ? "%u %s\r\n" : "%u %u\r\n";
1639 			i = u;
1640 		}
1641 	}
1642 	if (i == -1)
1643 	{
1644 		printf("501 header not in index\r\n");
1645 		return;
1646 	}
1647 
1648 	while ((c=*(cp)) && isspace(c)) cp++;
1649 	if (*cp)
1650 	{
1651 		u = strtoul(cp,&cp,10);
1652 		if (*cp == '-')
1653 		{
1654 			if (*(++cp)) l = strtoul(cp,&cp,10);
1655 			else l = grp->last;
1656 		}
1657 		else l = u;
1658 	}
1659 	else u = l = art;
1660 
1661 	if (!u)
1662 	{
1663 		printf("420 no article has been selected\r\n");
1664 		return;
1665 	}
1666 
1667 	patterns = malloc(sizeof(char const *));
1668 	pat_size = 1;
1669 	do
1670 	{
1671 		while (*cp && isspace(*cp)) ++cp;
1672 		if (*cp)
1673 		{
1674 			if (npats + 1 > pat_size)
1675 			{
1676 				patterns = realloc(patterns, (pat_size *= 2) *
1677 						sizeof(const char *));
1678 			}
1679 			patterns[npats++] = cp;
1680 			while (*cp && !isspace(*cp)) ++cp;
1681 			if (*cp) *(cp++) = 0;
1682 		}
1683 	} while (*cp);
1684 	if (!npats)
1685 	{
1686 		free(patterns);
1687 		printf("501 command syntax error\r\n");
1688 	}
1689 
1690 	printf("221 Header follows\r\n");
1691 
1692 	while (u <= l)
1693 	{
1694 		struct overview *o;
1695 
1696 		if ((o = find_overview(u)))
1697 		{
1698 			size_t pat;
1699 			char matchnum[12];
1700 			char *match = i < 5 ? (&o->subject)[i] :
1701 				(sprintf(matchnum, "%u", ((unsigned *)
1702 							  (&o->subject))[i]),
1703 				 matchnum);
1704 
1705 			for (pat = 0; pat < npats; ++pat)
1706 			{
1707 				if (match_pattern(patterns[pat], match))
1708 				{
1709 					printf("%u %s\r\n", u, match);
1710 				}
1711 			}
1712 		}
1713 		++u;
1714 	}
1715 
1716 	printf(".\r\n");
1717 	free(patterns);
1718 }
1719 
1720