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