1: /* 2: * This software is Copyright (c) 1986 by Rick Adams. 3: * 4: * Permission is hereby granted to copy, reproduce, redistribute or 5: * otherwise use this software as long as: there is no monetary 6: * profit gained specifically from the use or reproduction or this 7: * software, it is not sold, rented, traded or otherwise marketed, and 8: * this copyright notice is included prominently in any copy 9: * made. 10: * 11: * The author make no claims as to the fitness or correctness of 12: * this software for any use whatsoever, and it is provided as is. 13: * Any use of this software is at the user's own risk. 14: * 15: * inews - insert, receive, and transmit news articles. 16: * 17: */ 18: 19: #ifdef SCCSID 20: static char *SccsId = "@(#)inews.c 2.61 3/19/86"; 21: #endif /* SCCSID */ 22: 23: #include "iparams.h" 24: #include <errno.h> 25: 26: /* local defines for inews */ 27: 28: #define OPTION 0 /* pick up an option string */ 29: #define STRING 1 /* pick up a string of arguments */ 30: 31: #define UNKNOWN 0001 /* possible modes for news program */ 32: #define UNPROC 0002 /* Unprocessed input */ 33: #define PROC 0004 /* Processed input */ 34: #define CANCEL 0010 /* Cancel an article */ 35: #define CREATENG 0020 /* Create a new newsgroup */ 36: 37: char forgedname[NAMELEN]; /* A user specified -f option. */ 38: extern char histline[]; 39: /* Fake sys line in case they forget their own system */ 40: struct srec dummy_srec = { "MEMEME", "", "all", "", "" }; 41: 42: char *Progname = "inews"; /* used by xerror to identify failing program */ 43: 44: struct { /* options table. */ 45: char optlet; /* option character. */ 46: char filchar; /* if to pickup string, fill character. */ 47: int flag; /* TRUE if have seen this opt. */ 48: int oldmode; /* OR of legal input modes. */ 49: int newmode; /* output mode. */ 50: char *buf; /* string buffer */ 51: } *optpt, options[] = { /* 52: optlet filchar flag oldmode newmode buf */ 53: 't', ' ', FALSE, UNPROC, UNKNOWN, header.title, 54: 'n', NGDELIM, FALSE, UNPROC, UNKNOWN, header.nbuf, 55: 'd', '\0', FALSE, UNPROC, UNKNOWN, header.distribution, 56: 'e', ' ', FALSE, UNPROC, UNKNOWN, header.expdate, 57: 'p', '\0', FALSE, UNKNOWN,PROC, filename, 58: 'f', '\0', FALSE, UNPROC, UNKNOWN, forgedname, 59: 'F', ' ', FALSE, UNPROC, UNKNOWN, header.followid, 60: 'c', '\0', FALSE, UNKNOWN,CANCEL, filename, 61: 'C', '\0', FALSE, UNKNOWN,CREATENG, header.nbuf, 62: #define hflag options[9].flag 63: 'h', '\0', FALSE, UNPROC, UNKNOWN, filename, 64: #define oflag options[10].flag 65: 'o', '\0', FALSE, UNPROC, UNKNOWN, header.organization, 66: #define Mflag options[11].flag 67: 'M', '\0', FALSE, UNPROC, UNKNOWN, filename, 68: 'a', '\0', FALSE, UNPROC, UNKNOWN, header.approved, 69: '\0', '\0', 0, 0, 0, (char *)NULL 70: }; 71: 72: FILE *mailhdr(); 73: char *genversion(); 74: extern int errno; 75: 76: /* 77: * Authors: 78: * Matt Glickman glickman@ucbarpa.Berkeley.ARPA 79: * Mark Horton mark@cbosgd.UUCP 80: * Stephen Daniels swd@mcnc.UUCP 81: * Tom Truscott trt@duke.UUCP 82: * IHCC version adapted by: 83: * Larry Marek larry@ihuxf.UUCP 84: */ 85: main(argc, argv) 86: int argc; 87: register char **argv; 88: { 89: int state; /* which type of argument to pick up */ 90: int tlen, len; /* temps for string processing routine */ 91: register char *ptr; /* pointer to rest of buffer */ 92: int filchar; /* fill character (state = STRING) */ 93: char *user = NULL, *home = NULL; /* environment temps */ 94: struct passwd *pw; /* struct for pw lookup */ 95: struct group *gp; /* struct for group lookup */ 96: register int i; 97: FILE *mfd; /* mail file file-descriptor */ 98: char cbuf[BUFLEN]; /* command buffer */ 99: 100: /* uuxqt doesn't close all it's files */ 101: for (i = 3; !close(i); i++) 102: ; 103: /* set up defaults and initialize. */ 104: mode = UNKNOWN; 105: pathinit(); 106: ptr = rindex(*argv, '/'); 107: if (!ptr) 108: ptr = *argv - 1; 109: if (!strncmp(ptr+1, "rnews", 5)) { 110: mode = PROC; 111: #ifdef NICENESS 112: nice(NICENESS); 113: #endif /* NICENESS */ 114: } else if (argc < 2) 115: goto usage; 116: 117: state = OPTION; 118: header.title[0] = header.nbuf[0] = filename[0] = '\0'; 119: 120: /* check for existence of special files */ 121: if (!rwaccess(ARTFILE)) { 122: mfd = mailhdr((struct hbuf *)NULL, exists(ARTFILE) ? "Unwritable files!" : "Missing files!"); 123: if (mfd != NULL) { 124: #ifdef HIDDENNET 125: fprintf(mfd,"System: %s.%s\n\nThere was a problem with %s!!\n", LOCALSYSNAME, FULLSYSNAME, ARTFILE); 126: #else /* !HIDDENNET */ 127: fprintf(mfd,"System: %s\n\nThere was a problem with %s!!\n", FULLSYSNAME, ARTFILE); 128: #endif /* !HIDDENNET */ 129: (void) sprintf(cbuf, "touch %s;chmod 666 %s", ARTFILE, ARTFILE); 130: (void) system(cbuf); 131: if (rwaccess(ARTFILE)) 132: fprintf(mfd, "The problem has been taken care of.\n"); 133: else 134: fprintf(mfd, "Corrective action failed - check suid bits.\n"); 135: (void) mclose(mfd); 136: } 137: } 138: if (!rwaccess(ACTIVE)) { 139: mfd = mailhdr((struct hbuf *)NULL, exists(ACTIVE) ? "Unwritable files!" : "Missing files!"); 140: if (mfd != NULL) { 141: #ifdef HIDDENNET 142: fprintf(mfd,"System: %s.%s\n\nThere was a problem with %s!!\n", LOCALSYSNAME, FULLSYSNAME, ARTFILE); 143: #else /* !HIDDENNET */ 144: fprintf(mfd, "System: %s\n\nThere was a problem with %s!!\n", FULLSYSNAME, ACTIVE); 145: #endif /* !HIDDENNET */ 146: (void) sprintf(cbuf, "touch %s;chmod 666 %s", ACTIVE, ACTIVE); 147: (void) system(cbuf); 148: if (rwaccess(ACTIVE)) 149: fprintf(mfd, "The problem has been taken care of.\n"); 150: else 151: fprintf(mfd, "Corrective action failed - check suid bits.\n"); 152: (void) mclose(mfd); 153: } 154: } 155: SigTrap = FALSE; /* true if a signal has been caught */ 156: if (mode != PROC) { 157: (void) signal(SIGHUP, onsig); 158: (void) signal(SIGINT, onsig); 159: } 160: savmask = umask(N_UMASK); /* set up mask */ 161: uid = getuid(); 162: gid = getgid(); 163: duid = geteuid(); 164: dgid = getegid(); 165: if (uid == 0 && duid == 0) { 166: /* 167: * Must go through with this kludge since 168: * some systems do not honor the setuid bit 169: * when root invokes a setuid program. 170: */ 171: if ((pw = getpwnam(NEWSUSR)) == NULL) 172: xerror("Cannot get NEWSU pw entry"); 173: 174: duid = pw->pw_uid; 175: if ((gp = getgrnam(NEWSGRP)) == NULL) 176: xerror("Cannot get NEWSG gr entry"); 177: dgid = gp->gr_gid; 178: (void) setgid(dgid); 179: (void) setuid(duid); 180: } 181: 182: #ifndef IHCC 183: /* 184: * We force the use of 'getuser()' to prevent forgery of articles 185: * by just changing $LOGNAME 186: */ 187: if (isatty(fileno(stderr))) { 188: if ((user = getenv("USER")) == NULL) 189: user = getenv("LOGNAME"); 190: if ((home = getenv("HOME")) == NULL) 191: home = getenv("LOGDIR"); 192: } 193: #endif 194: if (user == NULL || home == NULL) 195: getuser(); 196: else { 197: if (username == NULL || username[0] == 0) { 198: username = AllocCpy(user); 199: } 200: userhome = AllocCpy(home); 201: } 202: getuser(); 203: 204: /* loop once per arg. */ 205: 206: ++argv; /* skip first arg, which is prog name. */ 207: 208: while (--argc) { 209: if (state == OPTION) { 210: if (**argv != '-') { 211: xerror("Bad option string \"%s\"", *argv); 212: } 213: while (*++*argv != '\0') { 214: for (optpt = options; optpt->optlet != '\0'; ++optpt) { 215: if (optpt->optlet == **argv) 216: goto found; 217: } 218: /* unknown option letter */ 219: usage: 220: fprintf(stderr, "usage: inews -t title"); 221: fprintf(stderr, " [ -n newsgroups ]"); 222: fprintf(stderr, " [ -e expiration date ]\n"); 223: fprintf(stderr, "\t[ -f sender]\n\n"); 224: xxit(1); 225: 226: found:; 227: if (optpt->flag == TRUE || (mode != UNKNOWN && 228: (mode&optpt->oldmode) == 0)) { 229: xerror("Bad %c option", **argv); 230: } 231: if (mode == UNKNOWN) 232: mode = optpt->newmode; 233: filchar = optpt->filchar; 234: optpt->flag = TRUE; 235: state = STRING; 236: ptr = optpt->buf; 237: len = BUFLEN; 238: } 239: 240: argv++; /* done with this option arg. */ 241: 242: } else { 243: 244: /* 245: * Pick up a piece of a string and put it into 246: * the appropriate buffer. 247: */ 248: if (**argv == '-') { 249: state = OPTION; 250: argc++; /* uncount this arg. */ 251: continue; 252: } 253: 254: if ((tlen = strlen(*argv)) >= len) 255: xerror("Argument string too long"); 256: (void) strcpy(ptr, *argv++); 257: ptr += tlen; 258: if (*(ptr-1) != filchar) 259: *ptr++ = filchar; 260: len -= tlen + 1; 261: *ptr = '\0'; 262: } 263: } 264: 265: /* 266: * ALL of the command line has now been processed. (!) 267: */ 268: 269: tty = isatty(fileno(stdin)); 270: 271: /* This code is really intended to be replaced by the control message. */ 272: if (mode == CANCEL) { 273: char *p; FILE *f; 274: f = xfopen(filename, "r"); 275: (void) hread(&header, f, TRUE); 276: p = index(header.path, ' '); 277: if (p != NULL) 278: *p = 0; 279: p = header.path; 280: if (strncmp(username, p, strlen(username)) 281: && uid != ROOTID && uid != geteuid() && uid) 282: xerror("Not contributor"); 283: cancel(); 284: xxit(0); 285: } 286: 287: if (*header.nbuf) { 288: lcase(header.nbuf); 289: ptr = index(header.nbuf, '\0'); 290: if (ptr[-1] == NGDELIM) 291: *--ptr = '\0'; 292: } 293: (void) nstrip(header.title); 294: (void) nstrip(header.expdate); 295: (void) nstrip(header.followid); 296: if (mode != PROC) { 297: getident(&header); 298: if (hflag) { 299: header.path[0] = '\0'; 300: (void) hread(&header, stdin, FALSE); 301: /* there are certain fields we won't let him specify. */ 302: if (header.from[0]) 303: (void) strcpy(forgedname, header.from); 304: if (!header.approved[0]) 305: Mflag = FALSE; 306: header.from[0] = '\0'; 307: header.subdate[0] = '\0'; 308: header.sender[0] = '\0'; 309: } 310: #ifdef MYORG 311: if (header.organization[0] == '\0' && !Mflag) { 312: strncpy(header.organization, MYORG, BUFLEN); 313: if (strncmp(header.organization, "Frobozz", 7) == 0) 314: header.organization[0] = '\0'; 315: if (ptr = getenv("ORGANIZATION")) 316: strncpy(header.organization, ptr, BUFLEN); 317: /* 318: * Note that the organization can also be turned off by 319: * setting it to the null string, either in MYORG or 320: * $ORGANIZATION in the environment. 321: */ 322: if (header.organization[0] == '/') { 323: mfd = fopen(header.organization, "r"); 324: if (mfd) { 325: (void) fgets(header.organization, sizeof header.organization, mfd); 326: (void) fclose(mfd); 327: } else { 328: header.organization[0] = '\0'; 329: logerr("Couldn't open %s", 330: header.organization); 331: } 332: ptr = index(header.organization, '\n'); 333: if (ptr) 334: *ptr = '\0'; 335: } 336: } 337: #endif /* MYORG */ 338: if (forgedname[0]) { 339: register char *p1; 340: if (Mflag) 341: sprintf(header.path, "%s!%s", 342: FULLSYSNAME, username); 343: else if (!header.path[0]) { 344: (void) strcpy(header.path, forgedname); 345: 346: if ((p1 = strpbrk(header.path, "@ (<")) != NULL) 347: *p1 = '\0'; 348: } 349: if (!Mflag && !strpbrk(forgedname, "@ (<")) 350: (void) sprintf(header.from,"%s@%s%s", 351: forgedname, FULLSYSNAME, MYDOMAIN); 352: else 353: (void) strncpy(header.from, forgedname, BUFLEN); 354: 355: (void) sprintf(header.sender, "%s@%s%s", 356: username, FULLSYSNAME, MYDOMAIN); 357: } else { 358: gensender(&header, username); 359: } 360: } 361: 362: /* Authorize newsgroups. */ 363: if (mode == PROC) { 364: #ifdef BATCH 365: checkbatch(); 366: #endif /* BATCH */ 367: (void) signal(SIGHUP, SIG_IGN); 368: (void) signal(SIGINT, SIG_IGN); 369: (void) signal(SIGQUIT, SIG_IGN); 370: header.ident[0] = '\0'; 371: if (hread(&header, stdin, TRUE) == NULL) 372: xerror("Inbound news is garbled"); 373: input(); 374: if (history(&header)) { 375: log("Duplicate article %s rejected. Path: %s", 376: header.ident, header.path); 377: xxit(0); 378: } 379: } 380: 381: /* Easy way to make control messages, since all.all.ctl is unblessed */ 382: if (mode != PROC && prefix(header.title, "cmsg ") && header.ctlmsg[0] == 0) 383: (void) strcpy(header.ctlmsg, &header.title[5]); 384: is_ctl = mode != CREATENG && 385: (ngmatch(header.nbuf, "all.all.ctl,") || header.ctlmsg[0]); 386: #ifdef DEBUG 387: fprintf(stderr,"is_ctl set to %d\n", is_ctl); 388: #endif 389: 390: /* Must end in comma (NGDELIM) */ 391: #define MODGROUPS "mod.all,all.mod,all.announce," 392: if (ngmatch(header.nbuf, MODGROUPS) && !header.approved[0]) { 393: mfd = mailhdr(&header, "Moderated newsgroup"); 394: if (mfd) { 395: fprintf(mfd, "This newsgroup is moderated, and cannot be posted to directly.\n"); 396: fprintf(mfd, "Please mail your article to the moderator for posting.\n"); 397: hwrite(&header, mfd); 398: if (infp) 399: while ((i = getc(infp)) != EOF) 400: putc(i, mfd); 401: (void) mclose(mfd); 402: } 403: xerror("Unapproved moderated newsgroup."); 404: } 405: 406: if (mode != CREATENG) { 407: if (!*header.title) 408: xerror("No title, ng %s from %s", header.nbuf, 409: header.from); 410: if (!*header.nbuf) 411: (void) strcpy(header.nbuf, DFLTNG); 412: } 413: 414: if (mode <= UNPROC) { 415: #ifdef FASCIST 416: if (uid && uid != ROOTID && fascist(user, header.nbuf)) 417: xerror("User %s is not authorized to post to newsgroup %s", user, header.nbuf); 418: #endif /* FASCIST */ 419: ctlcheck(); 420: } 421: 422: if (mode == CREATENG) 423: createng(); 424: /* Determine input. */ 425: if (mode != PROC) 426: input(); 427: 428: /* Do the actual insertion. */ 429: insert(); 430: } 431: 432: /* 433: * Create a newsgroup 434: */ 435: createng() 436: { 437: 438: /* 439: * Only certain users are allowed to create newsgroups 440: */ 441: if (uid != ROOTID && uid != geteuid() && uid) { 442: fprintf(stderr, "Please contact one of the local netnews people\n\tto create this group for you"); 443: xxit(1); 444: } 445: 446: (void) sprintf(bfr, "%s/inews -n %s.ctl -t cmsg newgroup %s -d local", 447: LIB, header.nbuf, header.nbuf); 448: printf("Please type in a paragraph describing the new newsgroup.\n"); 449: printf("End with control D as usual.\n"); 450: printf("%s\n", bfr); 451: (void) fflush(stdout); 452: (void) system(bfr); 453: exit(0); 454: /*NOTREACHED*/ 455: } 456: 457: char firstbufname[BUFLEN]; 458: /* 459: * Link ARTICLE into dir for ngname and update active file. 460: */ 461: long 462: localize(ngname) 463: char *ngname; 464: { 465: char afline[BUFLEN]; 466: long ngsize; 467: long fpos; 468: int e; 469: char *cp; 470: 471: lock(); 472: actfp = xfopen(ACTIVE, "r+"); 473: 474: for(;;) { 475: fpos = ftell(actfp); 476: if (fgets(afline, sizeof afline, actfp) == NULL) { 477: unlock(); 478: (void) fclose(actfp); 479: logerr("Can't fine \"%s\" in active file", ngname); 480: return FALSE; /* No such newsgroup locally */ 481: } 482: if (prefix(afline, ngname)) { 483: (void) sscanf(afline, "%s %ld", bfr, &ngsize); 484: if (strcmp(bfr, ngname) == 0) { 485: if (ngsize < 0 || ngsize > 99998) { 486: logerr("found bad ngsize %ld ng %s, setting to 1", ngsize, bfr); 487: ngsize = 1; 488: } 489: break; 490: } 491: } 492: } 493: for (;;) { 494: cp = dirname(ngname); 495: if (!exists(cp)) 496: mknewsg(cp, ngname); 497: 498: (void) sprintf(bfr, "%s/%ld", cp, ngsize+1); 499: #ifdef VMS 500: if (vmslink(ARTICLE, bfr) == 0) 501: break; 502: #else /* !VMS */ 503: if (link(ARTICLE, bfr) == 0) 504: break; 505: #endif /* !VMS */ 506: e = errno; /* keep log from clobbering it */ 507: log("Cannot install article as %s: %s", bfr, errmsg(errno)); 508: if (e != EEXIST) { 509: logerr("Link into %s failed (%s); check dir permissions.", 510: bfr, errmsg(e)); 511: unlock(); 512: (void) fclose(actfp); 513: return FALSE; 514: } 515: ngsize++; 516: } 517: 518: (void) fflush(actfp); 519: #ifdef VMS 520: (void) fclose(actfp); 521: vmstounix(ACTIVE); 522: actfp = fopen(ACTIVE, "r+"); 523: #endif /* VMS */ 524: 525: /* 526: * This works around a bug in the 4.1bsd stdio 527: * on fseeks to non even offsets in r+w files 528: */ 529: if (fpos&1) 530: (void) rewind(actfp); 531: 532: (void) fseek(actfp, fpos, 0); 533: /* Has to be same size as old because of %05d. 534: * This will overflow with 99999 articles. 535: */ 536: fprintf(actfp, "%s %05ld", ngname, ngsize+1); 537: if (ferror(actfp)) 538: xerror("Active file write failed"); 539: (void) fclose(actfp); 540: #ifdef VMS 541: unixtovms(ACTIVE); 542: #endif /* VMS */ 543: unlock(); 544: if (firstbufname[0] == '\0') 545: (void) strcpy(firstbufname, bfr); 546: (void) sprintf(bfr, "%s/%ld ", ngname, ngsize+1); 547: addhist(bfr); 548: return ngsize+1; 549: } 550: 551: /* 552: * Localize for each newsgroup and broadcast. 553: */ 554: insert() 555: { 556: register char *ptr; 557: register FILE *tfp; 558: register int c; 559: struct srec srec; /* struct for sys file lookup */ 560: struct tm *tm; 561: int is_invalid = FALSE; 562: int exitcode = 0; 563: long now; 564: #ifdef DOXREFS 565: register char *nextref = header.xref; 566: #endif /* DOXREFS */ 567: 568: /* Fill up the rest of header. */ 569: if (mode != PROC) { 570: history(&header); 571: } 572: dates(&header); 573: (void) time(&now); 574: tm = localtime(&now); 575: if (header.expdate[0]) 576: addhist(" "); 577: #ifdef USG 578: sprintf(bfr,"%2.2d/%2.2d/%d %2.2d:%2.2d\t", 579: #else /* !USG */ 580: sprintf(bfr,"%02d/%02d/%d %02d:%02d\t", 581: #endif /* !USG */ 582: tm->tm_mon+1, tm->tm_mday, tm->tm_year,tm->tm_hour, tm->tm_min); 583: addhist(bfr); 584: log("%s %s ng %s subj '%s' from %s", 585: mode==PROC ? "received" : "posted", 586: header.ident, header.nbuf, header.title, header.from); 587: 588: /* Clean up Newsgroups: line */ 589: if (!is_ctl && mode != CREATENG) 590: is_invalid = ngfcheck(mode == PROC); 591: 592: /* Write article to temp file. */ 593: tfp = xfopen(mktemp(ARTICLE), "w"); 594: 595: if (is_invalid) { 596: logerr("No valid newsgroups found, moved to junk"); 597: if (localize("junk")) 598: savehist(histline); 599: exitcode = 1; 600: goto writeout; 601: } 602: 603: if (time((time_t *)0) > (cgtdate(header.subdate) + HISTEXP) ){ 604: logerr("Article too old, moved to junk"); 605: if (localize("junk")) 606: savehist(histline); 607: exitcode = 1; 608: goto writeout; 609: } 610: 611: if (is_ctl) { 612: exitcode = control(&header); 613: if (localize("control") && exitcode != 0) 614: savehist(histline); 615: } else { 616: if (s_find(&srec, FULLSYSNAME) == FALSE) { 617: logerr("Cannot find my name '%s' in %s", FULLSYSNAME, SUBFILE); 618: srec = dummy_srec; 619: } 620: #ifdef DOXREFS 621: (void) strncpy(nextref, FULLSYSNAME, BUFLEN); 622: #endif /* DOXREFS */ 623: for (ptr = nbuf; *ptr;) { 624: if (ngmatch(ptr,srec.s_nbuf) || index(ptr,'.') == NULL){ 625: #ifdef DOXREFS 626: while (*nextref++) 627: ; 628: (void) sprintf(--nextref, " %s:%ld", ptr, localize(ptr)); 629: #else /* !DOXREFS */ 630: (void) localize(ptr); 631: #endif /* !DOXREFS */ 632: } 633: while (*ptr++) 634: ; 635: } 636: if (firstbufname[0] == '\0') { 637: logerr("Newsgroups in active, but not sys"); 638: (void) localize("junk"); 639: } 640: } 641: #ifdef DOXREFS 642: if (index(header.nbuf, NGDELIM) == NULL) 643: header.xref[0] = '\0'; 644: #endif /* DOXREFS */ 645: 646: writeout: 647: /* Part 1 of kludge to get around article truncation problem */ 648: if ( (c=getc(infp)) != EOF) { 649: ungetc(c, infp); 650: if (c == ' ' || c == '\t') { 651: header.intnumlines++; 652: (void) sprintf(header.numlines, "%d", 653: header.intnumlines); 654: } 655: } 656: /* End of part 1 */ 657: lhwrite(&header, tfp); 658: if ((c = getc(infp)) != EOF) { 659: /* Part 2 of kludge to get around article truncation problem */ 660: if (c == ' ' || c == '\t' ) 661: putc('\n', tfp); 662: /* End of part 2 */ 663: ungetc(c, infp); 664: while (fgets(bfr, BUFLEN, infp) != NULL) 665: fputs(bfr, tfp); 666: if (bfr[strlen(bfr)-1] != '\n') 667: putc('\n',tfp); 668: } 669: if (ferror(tfp)) 670: xerror("Write failed for temp file"); 671: (void) fclose(tfp); 672: (void) fclose(infp); 673: 674: if(exitcode == 0) { 675: int pid; 676: /* article has passed all the checks, so work in background */ 677: if (mode != PROC) 678: if ((pid=fork()) < 0) 679: xerror("Can't fork"); 680: else if (pid > 0) 681: exit(0); 682: #ifdef SIGTTOU 683: signal(SIGTTOU, SIG_IGN); 684: #endif /* SIGTTOU */ 685: savehist(histline); 686: broadcast(); 687: } 688: xxit(mode == PROC ? 0 : exitcode); 689: } 690: 691: input() 692: { 693: register char *cp; 694: register int c; 695: register int empty = TRUE; 696: FILE *tmpfp; 697: int consec_newlines = 0; 698: int linecount = 0; 699: int linserted = 0; 700: 701: tmpfp = xfopen(mktemp(INFILE), "w"); 702: if (*filename) { 703: tty = FALSE; 704: infp = xfopen(filename, "r"); 705: } else { 706: infp = stdin; 707: } 708: while (!SigTrap && fgets(bfr, BUFLEN, stdin) != NULL) { 709: if (mode == PROC) { /* zap trailing empty lines */ 710: #ifdef ZAPNOTES 711: if (empty && bfr[0] == '#' && bfr[2] == ':' 712: && header.nf_id[0] == '\0' 713: && header.nf_from[0] == '\0' ) { 714: (void) strcpy(header.nf_id, bfr); 715: (void) nstrip(header.nf_id); 716: (void) fgets(bfr, BUFLEN, stdin); 717: (void) strcpy(header.nf_from, bfr); 718: (void) nstrip(header.nf_from); 719: (void) fgets(bfr, BUFLEN, stdin); 720: 721: if (header.numlines[0]) { 722: header.intnumlines -= 2; 723: (void) sprintf(header.numlines, "%d", header.intnumlines); 724: } 725: 726: /* Strip trailing " - (nf)" */ 727: if ((cp = rindex(header.title, '-')) != NULL 728: && !strcmp(--cp, " - (nf)")) 729: *cp = '\0'; 730: log("Stripped notes header on %s", header.ident); 731: continue; 732: } 733: #endif /* ZAPNOTES */ 734: if (bfr[0] == '\n' || 735: /* Bandage for older versions of inews */ 736: bfr[1] == '\n' && !isascii(bfr[0])) { 737: consec_newlines++; /* count it, in case */ 738: continue; /* but don't write it*/ 739: } 740: /* foo! a non-empty line. write out all saved lines. */ 741: while (consec_newlines > 0) { 742: putc('\n', tmpfp); 743: consec_newlines--; 744: linecount++; 745: } 746: } 747: if (mode != PROC && tty && strcmp(bfr, ".\n") == 0) 748: break; 749: for (cp = bfr; c = toascii(*cp); cp++) { 750: if (isprint(c) || isspace(c) || c == '\b') 751: putc(c, tmpfp); 752: if (c == '\n') 753: linecount++; 754: } 755: if (bfr[0] == '>') 756: linserted++; 757: empty = FALSE; 758: } 759: if (*filename) 760: (void) fclose(infp); 761: if (mode != PROC && linserted > (linecount-linserted)) 762: xerror("Article rejected: More included text than new text"); 763: 764: if (mode != PROC && !is_ctl && header.sender[0] == '\0') { 765: int siglines = 0; 766: char sbuf[BUFLEN]; 767: (void) sprintf(bfr, "%s/%s", userhome, ".signature"); 768: if (access(bfr, 4) == 0) { 769: if ((infp = fopen(bfr, "r")) == NULL) { 770: (void) fprintf(stderr, 771: "inews: \"%s\" left off (must be readable by \"inews\" owner)\n", bfr); 772: goto finish; 773: } 774: 775: while (fgets(sbuf, sizeof sbuf, infp) != NULL) 776: if (++siglines > 4) 777: break; 778: if (siglines > 4) 779: fprintf(stderr,".signature not included (> 4 lines)\n"); 780: else { 781: rewind(infp); 782: fprintf(tmpfp, "-- \n"); /* To separate */ 783: linecount++; 784: while ((c = getc(infp)) != EOF) { 785: putc(c, tmpfp); 786: if (c == '\n') 787: linecount++; 788: } 789: } 790: (void) fclose(infp); 791: } 792: } 793: 794: finish: 795: if (ferror(tmpfp)) 796: xerror("write failed to temp file"); 797: (void) fclose(tmpfp); 798: if (SigTrap) { 799: if (tty) 800: fprintf(stderr, "Interrupt\n"); 801: if (tty && !empty) 802: fwait(fsubr(newssave, (char *) NULL, (char *) NULL)); 803: if (!tty) 804: log("Blown away by an interrupt %d", SigTrap); 805: xxit(1); 806: } 807: if (tty) 808: fprintf(stderr, "EOT\n"); 809: fflush(stdout); 810: infp = fopen(INFILE, "r"); 811: if (header.numlines[0]) { 812: /* 813: * Check line count if there's already one attached to 814: * the article. Could make this a fatal error - 815: * throwing it away if it got chopped, in hopes that 816: * another copy will come in later with a correct 817: * line count. But that seems a bit much for now. 818: */ 819: if (linecount != header.intnumlines) { 820: if (linecount == 0) 821: xerror("%s rejected. linecount expected %d, got 0", header.ident, header.intnumlines); 822: if (linecount > header.intnumlines || 823: linecount+consec_newlines < header.intnumlines) 824: log("linecount expected %d, got %d", header.intnumlines, linecount+consec_newlines); 825: } 826: /* adjust count for blank lines we stripped off */ 827: if (consec_newlines) { 828: header.intnumlines -= consec_newlines; 829: if (header.intnumlines < 0 ) 830: header.intnumlines = 0; /* paranoia */ 831: (void) sprintf(header.numlines, "%d", header.intnumlines); 832: } 833: 834: } else { 835: /* Attach a line count to the article. */ 836: header.intnumlines = linecount; 837: (void) sprintf(header.numlines, "%d", linecount); 838: } 839: } 840: 841: /* 842: * Make the directory for a new newsgroup. ngname should be the 843: * full pathname of the directory. Do the other stuff too. 844: * The various games with setuid and chown are to try to make sure 845: * the directory is owned by NEWSUSR and NEWSGRP, which is tough to 846: * do if you aren't root. This will work on a UCB system (which allows 847: * setuid(geteuid()) or a USG system (which allows you to give away files 848: * you own with chown), otherwise you have to change your kernel to allow 849: * one of these things or run with your dirs 777 so that it doesn't matter 850: * who owns them. 851: */ 852: mknewsg(fulldir, ngname) 853: char *fulldir; 854: char *ngname; 855: { 856: #ifdef USG 857: register char *p; 858: char parent[200]; 859: char sysbuf[200]; 860: struct stat sbuf; 861: #endif /* USG */ 862: 863: if (ngname == NULL || !isalpha(ngname[0])) 864: xerror("Tried to make illegal newsgroup %s", ngname); 865: 866: #ifdef USG 867: /* 868: * If the parent is 755 the setuid(getuid) 869: * will fail, and since mkdir is suid, and our real uid is random, 870: * the mkdir will fail. So we have to temporarily chmod it to 777. 871: */ 872: (void) strcpy(parent, fulldir); 873: while (p = rindex(parent, '/')) { 874: *p = '\0'; 875: if (stat(parent, &sbuf) == 0) { 876: (void) chmod(parent, 0777); 877: break; 878: } 879: } 880: #endif /* USG */ 881: 882: /* Create the directory */ 883: mkparents(fulldir); 884: if (mkdir(fulldir, 0777) < 0) 885: xerror("Cannot mkdir %s: %s", fulldir, errmsg(errno)); 886: 887: #ifdef USG 888: (void) chmod(parent, (int)sbuf.st_mode); /* put it back */ 889: /* 890: * Give away the directories we just created which were assigned 891: * our real uid. 892: */ 893: (void) setuid(uid); 894: (void) chown(fulldir, duid, dgid); 895: (void) strcpy(sysbuf, fulldir); 896: while (p = rindex(sysbuf, '/')) { 897: *p = '\0'; 898: /* stop when get to last known good parent */ 899: if (strcmp(sysbuf, parent) == 0) 900: break; 901: (void) chown(sysbuf, duid, dgid); 902: } 903: (void) setuid(duid); 904: #endif /* USG */ 905: 906: log("make newsgroup %s in dir %s", ngname, fulldir); 907: } 908: 909: /* 910: * If any parent directories of this dir don't exist, create them. 911: */ 912: mkparents(dname) 913: char *dname; 914: { 915: char buf[200]; 916: register char *p; 917: 918: (void) strcpy(buf, dname); 919: p = rindex(buf, '/'); 920: if (p) 921: *p = '\0'; 922: if (exists(buf)) 923: return; 924: mkparents(buf); 925: if (mkdir(buf, 0777) < 0) 926: xerror("Can not mkdir %s: %s", buf, errmsg(errno)); 927: } 928: 929: cancel() 930: { 931: register FILE *fp; 932: 933: log("cancel article %s", filename); 934: fp = fopen(filename, "r"); 935: if (fp == NULL) { 936: log("article %s not found", filename); 937: return; 938: } 939: if (hread(&header, fp, TRUE) == NULL) 940: xerror("Article is garbled."); 941: (void) fclose(fp); 942: (void) unlink(filename); 943: }