1: /* 2: ** nntpxmit - transmit netnews articles across the internet with nntp 3: ** 4: ** This program is for transmitting netnews between sites that offer the 5: ** NNTP service, internet style. Ideally, there are two forms of 6: ** transmission that can be used in this environment, since the 7: ** communication is interactive (and relatively immediate, when compared 8: ** with UUCP). They are: passive poll (what have you gotten lately?) and 9: ** active send (I have `x', do you want it?). The USENET as a whole 10: ** uniformly uses active send, and where the communication is batched 11: ** (e.g. UUCP, or electronic mail) the software sends without even asking, 12: ** unless it can determine that the article has already been to some site. 13: ** 14: ** It turns out that when you implement passive poll, you have to be 15: ** *very* careful about what you (the server) tell the client, because 16: ** something that you might wish to restrict distribution of (either 17: ** internal newsgroups, or material posted in the international groups in 18: ** local distributions) will otherwise leak out. It is the case that if 19: ** the server doesn't tell the client that article `x' is there, the 20: ** client won't ask for it. If the server tells about an article which 21: ** would otherwise stay within some restricted distribution, the onus is 22: ** then on the client to figure out that the article is not appropriate to 23: ** post on its local system. Of course, at that point, we have already 24: ** wasted the network bandwidth in transferring the article... 25: ** 26: ** This is a roundabout way of saying that this program only implements 27: ** active send. There will have to be once-over done on the NNTP spec 28: ** before passive poll goes in, because of the problems that I have cited. 29: ** 30: ** Erik E. Fair <ucbvax!fair>, Oct 14, 1985 31: */ 32: 33: #include <stdio.h> 34: #include <sys/types.h> 35: #include <sys/file.h> 36: #include <syslog.h> 37: #include <sysexits.h> 38: #include "defs.h" 39: #include "header.h" 40: #include "response_codes.h" 41: #include "nntpxmit.h" 42: 43: char *pname; 44: char Debug = FALSE; 45: char Do_Stats = TRUE; 46: char *USAGE = "USAGE: nntpxmit [-s] hostname:file [hostname:file ...]"; 47: FILE *getfp(); 48: 49: struct Stats { 50: u_long offered; 51: u_long accepted; 52: u_long rejected; 53: u_long failed; 54: } Stats = {0L, 0L, 0L, 0L}; 55: 56: extern int errno; 57: extern char *rindex(); 58: extern char *index(); 59: extern char *errmsg(); 60: 61: main(ac,av) 62: int ac; 63: char *av[]; 64: { 65: register int i; 66: char *host, *file; 67: 68: pname = ((pname = rindex(av[0],'/')) ? pname + 1 : av[0]); 69: 70: if (ac < 2) { 71: fprintf(stderr,"%s: %s\n", pname, USAGE); 72: exit(EX_USAGE); 73: } 74: 75: /* note that 4.2 BSD openlog has only two args */ 76: (void) openlog(pname, LOG_PID, LOG_LOCAL7); 77: 78: for(i = 1; i < ac; i++) { 79: if (av[i][0] == '-') { 80: switch(av[i][1]) { 81: case 's': 82: Do_Stats = FALSE; 83: break; 84: case 'd': 85: Debug++; 86: break; 87: default: 88: fprintf(stderr,"%s: no such option: -%c\n", 89: pname, av[i][1]); 90: fprintf(stderr,"%s: %s\n", pname, USAGE); 91: exit(EX_USAGE); 92: } 93: continue; 94: } 95: 96: /* 97: ** OK, it wasn't an option, therefore it must be a 98: ** hostname, filename pair. 99: */ 100: host = av[i]; 101: if ((file = index(host, ':')) != (char *)NULL) { 102: *file++ = '\0'; 103: } else { 104: fprintf(stderr,"%s: illegal hostname:file pair: <%s>\n", 105: pname, host); 106: continue; 107: } 108: 109: bzero(&Stats, sizeof(Stats)); 110: if (sendnews(host, file) && Do_Stats) { 111: syslog(LOG_INFO, 112: "%s stats %d offered %d accepted %d rejected %d failed\n", 113: host, Stats.offered, Stats.accepted, Stats.rejected, Stats.failed); 114: } 115: } 116: exit(EX_OK); 117: } 118: 119: /* 120: ** Given a hostname to connect to, and a file of filenames (which contain 121: ** netnews articles), send those articles to the named host using NNTP. 122: */ 123: sendnews(host, file) 124: char *host, *file; 125: { 126: register int code; 127: register FILE *filefile = fopen(file, "r"); 128: register FILE *fp; 129: char buf[BUFSIZ]; 130: 131: /* 132: ** if no news to send, return 133: */ 134: if (filefile == (FILE *)NULL) { 135: dprintf(stderr, "%s: %s: %s\n", pname, file, errmsg(errno)); 136: return(FALSE); 137: } 138: 139: if (hello(host) == FAIL) { 140: fclose(filefile); 141: return(FALSE); 142: } 143: 144: while((fp = getfp(filefile)) != (FILE *)NULL) { 145: switch(code = ihave(fp)) { 146: case CONT_XFER: 147: if (!sendfile(fp)) { 148: fprintf(stderr, "%s: %s: article transmission failed.\n", pname, host); 149: if (Do_Stats) Stats.failed++; 150: fclose(filefile); 151: fclose(fp); 152: goodbye(DONT_WAIT); 153: return(TRUE); 154: } 155: fclose(fp); 156: /* 157: ** Here I read the reply from the remote about the 158: ** transferred article, and I throw it away. I 159: ** should probably try and record the article 160: ** filename and append it back to the batchfile 161: ** again in the name of reliability, but that's 162: ** messy, and it's easier to assume that the guy 163: ** will have redundant feeds. 164: */ 165: code = readreply(buf, sizeof(buf)); 166: if (Do_Stats && code != OK_XFERED) Stats.failed++; 167: break; 168: case ERR_GOTIT: 169: fclose(fp); 170: break; 171: default: 172: fprintf(stderr,"%s: %s gave an improper response to IHAVE: %d\n", pname, host, code); 173: fclose(filefile); 174: fclose(fp); 175: goodbye(DONT_WAIT); 176: return(TRUE); 177: } 178: } 179: fclose(filefile); 180: if (unlink(file) < 0) { 181: fprintf(stderr,"%s: unable to unlink(%s): %s\n", pname, file, errmsg(errno)); 182: } 183: goodbye(WAIT); 184: return(TRUE); 185: } 186: 187: /* 188: ** Read the header of a netnews article, snatch the message-id therefrom, 189: ** and ask the remote if they have that one already. 190: */ 191: ihave(fp) 192: FILE *fp; 193: { 194: struct hbuf header; 195: char scr[LBUFLEN]; 196: 197: bzero(&header, sizeof(header)); 198: if (rfc822read(&header, fp, scr)) { 199: register int code; 200: char buf[BUFSIZ]; 201: 202: /* 203: ** If an article shows up without a message-id, 204: ** we scream bloody murder. That's one in 205: ** the `can't ever happen' category. 206: */ 207: if (header.ident[0] == '\0') { 208: fprintf(stderr, "%s: article w/o message-id!\n", pname); 209: return(ERR_GOTIT); 210: } 211: sprintf(buf, "IHAVE %s", header.ident); 212: if (Do_Stats) Stats.offered++; 213: 214: switch(code = converse(buf, sizeof(buf))) { 215: case CONT_XFER: 216: if (Do_Stats) Stats.accepted++; 217: rewind(fp); 218: return(code); 219: default: 220: if (Do_Stats) Stats.rejected++; 221: return(code); 222: } 223: } 224: /* 225: ** something botched locally with the article 226: ** so we don't send it, but we don't break off 227: ** communications with the remote either. 228: */ 229: return(ERR_GOTIT); 230: } 231: 232: /* 233: ** Given that fp points to an open file containing filenames, 234: ** open and return a file pointer to the next filename in the file. 235: ** Don't you love indirection? 236: */ 237: FILE * 238: getfp(fp) 239: FILE *fp; 240: { 241: register FILE *newfp = NULL; 242: register char *cp; 243: char filename[BUFSIZ]; 244: 245: while(newfp == NULL) { 246: if (fgets(filename, sizeof(filename), fp) == NULL) 247: return(NULL); /* EOF, tell caller */ 248: 249: /* zap \n char */ 250: if (*(cp = &filename[strlen(filename) - 1]) == '\n') 251: *cp = '\0'; 252: 253: if ((newfp = fopen(filename, "r")) == NULL) 254: perror(filename); /* tell 'em why it failed */ 255: } 256: dprintf(stderr, "FILE: %s\n", filename); /* DEBUG */ 257: return(newfp); 258: }