1: /* 2: * nntpxfer 3: * 4: * Connects to the specified nntp server, and transfers all new news 5: * since the last successful invocation. 6: * 7: * last successful invocation date and time are stored in a file at 8: * /usr/spool/news/nntp.<hostname> as 9: * groups YYMMDD HHMMSS distributions\n 10: * in case you need to edit it. You can also override this on 11: * the command line in the same format, in which case the file won't 12: * be updated. 13: * 14: * Brian Kantor, UCSD 1986 15: * (some bug fixes by ambar@athena.mit.edu) 16: */ 17: 18: #define DEBUG 19: 20: /* you'd think that 4096 articles at one go is enough.... */ 21: #define MAXARTS 4096 22: 23: #include <sys/types.h> 24: #include <sys/dir.h> 25: #include <sys/socket.h> 26: #include <sys/stat.h> 27: #include <sys/ioctl.h> 28: #include <sys/file.h> 29: #include <sys/time.h> 30: #include <sys/wait.h> 31: #include <sys/resource.h> 32: 33: #include <net/if.h> 34: #include <netinet/in.h> 35: 36: #include <stdio.h> 37: #include <errno.h> 38: #include <ctype.h> 39: #include <netdb.h> 40: #include <signal.h> 41: #include <dbm.h> 42: 43: #define INEWS "/usr/lib/news/inews -p" 44: #define HIST "/usr/lib/news/history" 45: 46: char *malloc(); 47: char *strcpy(); 48: char *strcat(); 49: long time(); 50: u_long inet_addr(); 51: 52: extern int errno; 53: char *artlist[MAXARTS]; 54: int server; /* stream socket to the nntp server */ 55: int newart, dupart, misart; 56: 57: main(argc, argv) 58: int argc; 59: char *argv[]; 60: { 61: FILE *dtfile; /* where last xfer date/time stored */ 62: char buf[BUFSIZ]; 63: char lastdate[16]; 64: char distributions[BUFSIZ]; 65: char dtname[128]; 66: char newsgroups[BUFSIZ]; 67: char lasttime[16]; 68: int connected = 0; /* 1 = connected */ 69: int i; 70: int omitupdate = 0; /* 1 = don't update datetime */ 71: long clock; 72: long newdate, newtime; 73: struct hostent *hp; 74: struct servent *sp; 75: struct sockaddr_in sin; 76: struct tm *now; 77: 78: /* OPTIONS 79: argv[1] MUST be the host name 80: argv[2-4] MAY be "newsgroups YYMMDD HHMMSS" 81: argv[5] MAY be distributions 82: (otherwise use 2-4/5 from the file 83: "/usr/spool/news/nntp.hostname") 84: */ 85: 86: if (argc != 2 && argc != 5 && argc != 6) 87: { 88: (void) printf("Usage: %s host [groups YYMMDD HHMMSS [<dist>]]\n", 89: argv[0]); 90: exit(1); 91: } 92: 93: if (argc > 2) 94: { 95: omitupdate++; 96: (void) strcpy(newsgroups, argv[2]); 97: (void) strcpy(lastdate, argv[3]); 98: (void) strcpy(lasttime, argv[4]); 99: (void) strcpy(distributions, ""); 100: if (argc > 5) 101: (void) strcpy(distributions, argv[5]); 102: } 103: else 104: { 105: (void) strcpy(dtname, "/usr/spool/news/nntp."); 106: (void) strcat(dtname, argv[1]); 107: dtfile = fopen(dtname, "r"); 108: if (dtfile == NULL) 109: { 110: (void) printf("%s not found; using * 860101 000000 \n", 111: dtname); 112: (void) strcpy(newsgroups, "*"); 113: (void) strcpy(lastdate, "860101"); 114: (void) strcpy(lasttime, "000000"); 115: (void) strcpy(distributions, ""); 116: } 117: else 118: { 119: if (fscanf(dtfile, "%s %s %s %s", 120: newsgroups, lastdate, lasttime, distributions) < 3) 121: { 122: (void) printf("%s invalid; using * 860101 000000\n", 123: dtname); 124: (void) strcpy(newsgroups, "*"); 125: (void) strcpy(lastdate, "860101"); 126: (void) strcpy(lasttime, "000000"); 127: (void) strcpy(distributions, ""); 128: } 129: (void) fclose(dtfile); 130: } 131: clock = time((long *)0); 132: now = gmtime(&clock); 133: newdate = (now->tm_year * 10000) + 134: ((now->tm_mon + 1) * 100) + now->tm_mday; 135: newtime = (now->tm_hour * 10000) + 136: (now->tm_min * 100) + now->tm_sec; 137: } 138: 139: #ifdef DEBUG 140: (void) printf("newsgroups = '%s'\n", newsgroups); 141: (void) printf("date = '%s'\n", lastdate); 142: (void) printf("time = '%s'\n", lasttime); 143: (void) printf("distributions = '%s'\n", distributions); 144: (void) printf("now is = %06d %06d\n", newdate, newtime); 145: #endif 146: 147: if (dbminit(HIST) < 0) 148: { 149: perror("couldn't open history file"); 150: exit(1); 151: } 152: 153: sin.sin_addr.s_addr = inet_addr(argv[1]); 154: if (sin.sin_addr.s_addr != -1) 155: { 156: sin.sin_family = AF_INET; 157: } 158: else 159: { 160: hp = gethostbyname(argv[1]); 161: if (hp == NULL) 162: { 163: (void) printf("%s: unknown host\n", argv[1]); 164: exit(1); 165: } 166: 167: sin.sin_family = hp->h_addrtype; 168: #ifdef BSD43 169: bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr, 170: hp->h_length); 171: #else BSD43 172: bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, 173: hp->h_length); 174: #endif BSD43 175: } 176: 177: sp = getservbyname("nntp", "tcp"); 178: if (sp == NULL) 179: { 180: perror("nntp/tcp"); 181: exit(1); 182: } 183: 184: sin.sin_port = sp->s_port; 185: 186: do { 187: server = socket(AF_INET, SOCK_STREAM, 0); 188: if (server < 0) 189: { 190: perror("nntpxfer: socket"); 191: exit(1); 192: } 193: 194: if (connect(server, (struct sockaddr *)&sin, sizeof (sin)) < 0) 195: { 196: #ifdef BSD43 197: if (hp && hp->h_addr_list[1]) 198: { 199: hp->h_addr_list++; 200: bcopy(hp->h_addr_list[0], 201: (caddr_t)&sin.sin_addr, hp->h_length); 202: (void) close(server); 203: continue; 204: } 205: #endif BSD43 206: perror("nntpxfer: connect"); 207: exit(1); 208: } 209: connected++; 210: } 211: while (connected == 0); 212: 213: #ifdef DEBUG 214: (void) printf("connected to nntp server at %s\n", argv[1]); 215: #endif 216: /* 217: * ok, at this point we're connected to the nntp daemon 218: * at the distant host. 219: */ 220: 221: /* get the greeting herald */ 222: (void) sockread(buf); 223: #ifdef DEBUG 224: (void) printf("%s\n", buf); 225: #endif 226: if (buf[0] != '2') /* uh-oh, something's wrong! */ 227: { 228: (void) printf("protocol error: got '%s'\n", buf); 229: (void) close(server); 230: exit(1); 231: } 232: 233: 234: /* first, tell them we're a slave process to get priority */ 235: sockwrite("SLAVE"); 236: (void) sockread(buf); 237: #ifdef DEBUG 238: (void) printf("%s\n", buf); 239: #endif 240: if (buf[0] != '2') /* uh-oh, something's wrong! */ 241: { 242: (void) printf("protocol error: got '%s'\n", buf); 243: (void) close(server); 244: exit(1); 245: } 246: 247: /* now, ask for a list of new articles */ 248: if (strlen(distributions)) 249: (void) sprintf(buf,"NEWNEWS %s %s %s GMT <%s>", 250: newsgroups, lastdate, lasttime, distributions); 251: else 252: (void) sprintf(buf,"NEWNEWS %s %s %s GMT", 253: newsgroups, lastdate, lasttime); 254: sockwrite(buf); 255: (void) sockread(buf); 256: #ifdef DEBUG 257: (void) printf("%s\n", buf); 258: #endif 259: if (buf[0] != '2') /* uh-oh, something's wrong! */ 260: { 261: (void) printf("protocol error: got '%s'\n", buf); 262: (void) close(server); 263: exit(1); 264: } 265: /* and here comes the list, terminated with a "." */ 266: #ifdef DEBUG 267: (void) printf("data\n"); 268: #endif 269: while (1) 270: { 271: (void) sockread(buf); 272: if (!strcmp(buf,".")) 273: break; 274: if (wewant(buf)) 275: { 276: if (newart > MAXARTS) 277: { 278: omitupdate++; 279: continue; 280: } 281: artlist[newart] = malloc((unsigned)(strlen(buf)+1)); 282: (void) strcpy(artlist[newart], buf); 283: newart++; 284: } 285: else 286: dupart++; 287: } 288: #ifdef DEBUG 289: (void) printf(".\n%d new, %d dup articles\n", newart, dupart); 290: #endif 291: 292: /* now that we know which articles we want, retrieve them */ 293: for (i=1; i < newart; i++) 294: (void) artfetch(artlist[i]); 295: 296: #ifdef DEBUG 297: (void) printf("%d missing articles\n", misart); 298: #endif 299: /* we're all done, so tell them goodbye */ 300: sockwrite("QUIT"); 301: (void) sockread(buf); 302: #ifdef DEBUG 303: (void) printf("%s\n", buf); 304: #endif 305: if (buf[0] != '2') /* uh-oh, something's wrong! */ 306: { 307: (void) printf("error: got '%s'\n", buf); 308: (void) close(server); 309: exit(1); 310: } 311: (void) close(server); 312: 313: /* do we want to update the timestamp file? */ 314: if (!omitupdate) 315: { 316: (void) sprintf(buf, "%s %06d %06d %s\n", 317: newsgroups, newdate, newtime, distributions); 318: #ifdef DEBUG 319: (void) printf("updating %s:\n\t%s\n", dtname, buf); 320: #endif 321: dtfile = fopen(dtname, "w"); 322: if (dtfile == NULL) 323: { 324: perror(dtname); 325: exit(1); 326: } 327: (void) fputs(buf,dtfile); 328: (void) fclose(dtfile); 329: } 330: exit(0); 331: } 332: 333: artfetch(articleid) 334: char *articleid; 335: { 336: int lines = 0; 337: char buf[BUFSIZ]; 338: FILE *inews; 339: 340: /* now, ask for the article */ 341: (void) sprintf(buf,"ARTICLE %s", articleid); 342: sockwrite(buf); 343: (void) sockread(buf); 344: #ifdef DEBUG 345: (void) printf("%s\n", buf); 346: #endif 347: if (buf[0] == '4') /* missing article, just skipit */ 348: { 349: misart++; 350: return(0); 351: } 352: 353: if (buf[0] != '2') /* uh-oh, something's wrong! */ 354: { 355: (void) printf("protocol error: got '%s'\n", buf); 356: (void) close(server); 357: exit(1); 358: } 359: #ifdef DEBUG 360: (void) printf("command: %s\n", INEWS); 361: #endif 362: if ( (inews = popen(INEWS, "w")) == NULL) 363: { 364: perror(INEWS); 365: exit(1); 366: } 367: 368: /* and here comes the article, terminated with a "." */ 369: #ifdef DEBUG 370: (void) printf("data\n"); 371: #endif 372: while (1) 373: { 374: (void) sockread(buf); 375: if (buf[0] == '.' && buf[1] == '\0') 376: break; 377: lines++; 378: (void) strcat(buf,"\n"); 379: (void) fputs(((buf[0] == '.') ? buf + 1 : buf), 380: inews); 381: } 382: #ifdef DEBUG 383: (void) printf(".\n%d lines\n", lines); 384: #endif 385: (void) fflush(inews); 386: (void) pclose(inews); 387: return(0); 388: } 389: 390: int 391: sockread(buf) 392: char *buf; 393: { 394: char c; 395: int j = 0; 396: #ifdef BSD43 397: fd_set rf; 398: #else BSD43 399: int rf; 400: #endif BSD43 401: struct timeval tv; 402: int r; 403: char *p = buf; 404: 405: while ( 1 ) 406: { 407: tv.tv_sec = 1800; /* 15 minutes */ 408: tv.tv_usec = 0L; 409: #ifdef BSD43 410: FD_ZERO(&rf); 411: FD_SET(server, &rf); 412: #else BSD43 413: rf = 1 << server; 414: #endif BSD43 415: r = select(20, (fd_set *)&rf, (fd_set *)0, (fd_set *)&rf, &tv); 416: 417: if (r < 0) 418: { 419: if (errno == EINTR) 420: continue; 421: perror("getsock select"); 422: exit(1); 423: } 424: if (r == 0) 425: { 426: printf("read timed out.\n"); 427: exit(1); 428: } 429: 430: if (read(server, &c, 1) <= 0) 431: break; 432: 433: /* mask off any chance parity bits */ 434: *p = c & 0x7f; 435: 436: /* look for end of line (== LF) */ 437: if (c == 0x0a) 438: { 439: if (j > 0 && *(p-1) == 0x0d) 440: *(p-1) = '\0'; 441: else 442: *p = '\0'; 443: return(strlen(buf)); 444: } 445: j++; p++; 446: } 447: perror("sockread"); 448: (void) close(server); 449: exit(1); 450: /* NOTREACHED */ 451: } 452: 453: sockwrite(buf) 454: char *buf; 455: { 456: register int sz; 457: char buf2[BUFSIZ]; 458: #ifdef DEBUG 459: (void) printf(">>> %s\n", buf); 460: #endif 461: (void) strcpy(buf2,buf); 462: (void) strcat(buf2,"\r\n"); 463: sz = strlen(buf2); 464: if (write(server,buf2,sz) != sz) 465: { 466: (void) printf("write error on server socket\n"); 467: (void) close(server); 468: exit(1); 469: } 470: } 471: 472: int 473: wewant(articleid) 474: char *articleid; 475: { 476: datum k, d; 477: char id[BUFSIZ]; 478: char *p; 479: 480: /* remove any case sensitivity */ 481: (void) strcpy(id, articleid); 482: p = id; 483: while (*p) 484: { 485: if (isupper(*p)) 486: *p = tolower(*p); 487: p++; 488: } 489: 490: k.dptr = id; 491: k.dsize = strlen(articleid) + 1; 492: 493: d = fetch(k); 494: 495: if (d.dptr) 496: { 497: #ifdef DEBUG 498: (void) printf("dup: '%s'\n", articleid); 499: #endif 500: return(0); 501: } 502: 503: #ifdef DEBUG 504: (void) printf("new: '%s'\n", articleid); 505: #endif 506: return(1); 507: }