/* $Id: nntpgrab.c,v 1.2 2005/09/11 17:18:18 bjk Exp $ */ /* * Connect to an NNTP server and grab the specified article by Message_ID. The * server and port may be specified on the command line or gotten from the * NNTPSERVER environment variable. * * $Log: nntpgrab.c,v $ * Revision 1.2 2005/09/11 17:18:18 bjk * Treat response codes 500 and 503 as server unavailable. * * Revision 1.1 2005/06/02 19:13:45 bjk * Initial commit. * * * Ben Kibbey */ #include #include #include #include #include #include #include #include #include #include #include #include #define NNTPPORT 119 #define BUFFER_SIZE 256 int debug; int sfd; char *username, *password; void usage(const char *pn) { printf("Usage: %s [-hkd] [-s [-p -e ] [...]\n", pn); printf(" -s Server to connect to. Default is $NNTPSERVER.\n"); printf(" -p Server port. Default is %i.\n", NNTPPORT); printf(" -k Don't strip any CTRL-LF from the article.\n"); printf(" -u Authenticate username.\n"); printf(" -e Password to authenticate user.\n"); printf(" -d Enable debugging.\n"); printf(" -h This help text.\n"); exit(EXIT_FAILURE); } int send_to_server(int fd, const char *str) { size_t n; if ((n = send(fd, str, strlen(str), 0)) != strlen(str)) { warn("send()"); return 1; } if (debug) fprintf(stderr, "%i >> '%s'\n", n, str); return 0; } int grab_article(FILE *fp, char *msgid, int strip) { struct timeval tv; int article_len = 0; int requested_article = 0; char *article = NULL; static int once; while (1) { fd_set r_fds, w_fds; char buf[BUFFER_SIZE]; int len = 0, n; char *p; int resp; FD_ZERO(&w_fds); FD_ZERO(&r_fds); FD_SET(sfd, &w_fds); FD_SET(sfd, &r_fds); tv.tv_sec = 0; tv.tv_usec = 300000; if ((n = select(sfd + 1, &r_fds, &w_fds, NULL, &tv)) > 0) { if (once) goto next_article; if (FD_ISSET(sfd, &r_fds)) { while ((p = fgets(buf, sizeof(buf), fp)) != NULL) { len = strlen(p); if (requested_article) { /* End of article. */ if (len >= 3 && ((p[0] == '.' && p[1] == '\r' && p[2] == '\n') || (p[0] == '.' && p[1] == '.' && p[2] == '\r' && p[2] == '\n'))) { printf("%s", article); free(article); once = 1; return EXIT_SUCCESS; } else { article_len += len + 1; if ((article = realloc(article, article_len * sizeof(char))) == NULL) err(EXIT_FAILURE, "realloc()"); if (strip) { p[strlen(p) - 2] = '\n'; p[strlen(p) - 1] = '\0'; } strcat(article, p); article[article_len - 1] = '\0'; } continue; } else { if (debug) fprintf(stderr, "%i << %s\n", len, buf); p[3] = '\0'; resp = atoi(p); p[3] = ' '; } if (resp == 200 || resp == 201) { authenticate: if (username) { snprintf(buf, sizeof(buf), "AUTHINFO USER %s\r\n", username); if (send_to_server(sfd, buf) != 0) { fprintf(stderr, "closing connection\n"); return EXIT_FAILURE; } continue; } next_article: if (debug) fprintf(stderr, "Fetching article %s ...\n", msgid); snprintf(buf, sizeof(buf), "ARTICLE %s\r\n", msgid); if (send_to_server(sfd, buf) != 0) { fprintf(stderr, "closing connection\n"); return EXIT_FAILURE; } } else if (resp >= 420 && resp < 430) { fprintf(stderr, "Invalid article syntax \"%s\".\n", msgid); return EXIT_FAILURE; } else if (resp >= 430 && resp < 440) { fprintf(stderr, "No such article \"%s\".\n", msgid); return EXIT_FAILURE; } else if (resp >= 220 && resp < 230) { requested_article = 1; article_len = 0; } else if (resp == 205) { return EXIT_SUCCESS; } else if (resp == 281) { if (debug) fprintf(stderr, "Successfully authenticated.\n"); goto next_article; } else if (resp == 480) { if (!username) { fprintf(stderr, "Authorization required.\n"); return EXIT_FAILURE; } goto authenticate; } else if (resp >= 410 && resp < 420) { fprintf(stderr, "Not in a newsgroup (invalid article \"%s\"?).\n", msgid); return EXIT_FAILURE; } else if (resp == 482) { fprintf(stderr, "Authentication for user \"%s\" rejected.\n", username); return EXIT_FAILURE; } else if (resp == 502) { fprintf(stderr, "Failed to authenticate user \"%s\".\n", username); return EXIT_FAILURE; } else if (resp == 503 || resp == 500) { fprintf(stderr, "Server unavailable?\n"); return EXIT_FAILURE; } else if (resp == 381) { snprintf(buf, sizeof(buf), "AUTHINFO PASS %s\r\n", password); if (send_to_server(sfd, buf) != 0) { fprintf(stderr, "closing connection\n"); return EXIT_FAILURE; } continue; } } if (len < 0) warn("recv()"); } } else printf("timeout\n"); } return EXIT_SUCCESS; } int main(int argc, char *argv[]) { int opt; struct sockaddr_in d_addr; char *host = NULL; int port = NNTPPORT; struct hostent *he; int ret = EXIT_SUCCESS; char buf[LINE_MAX], *p; FILE *fp; int strip = 1; char *tmp; host = getenv("NNTPSERVER"); if ((tmp = strchr(host, ':')) != NULL) { tmp++; port = atoi(tmp); host[strlen(host) - strlen(tmp) - 1] = '\0'; } while ((opt = getopt(argc, argv, "u:e:dkhs:p:")) != -1) { switch (opt) { case 'u': username = strdup(optarg); break; case 'e': password = strdup(optarg); break; case 'k': strip = 0; break; case 'd': debug = 1; break; case 's': host = strdup(optarg); break; case 'p': port = atoi(optarg); break; case 'h': default: usage(argv[0]); } } if (!host || (username && !password)) usage(argv[0]); if ((sfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) err(EXIT_FAILURE, "socket()"); if ((fp = fdopen(sfd, "rw")) == NULL) err(EXIT_FAILURE, "fdopen()"); memset(&d_addr, '\0', sizeof(struct sockaddr_in)); d_addr.sin_family = AF_INET; d_addr.sin_port = htons(port); if ((he = gethostbyname(host)) == NULL) errx(EXIT_FAILURE, "gethostbyname() failed."); d_addr.sin_addr = *((struct in_addr *)he->h_addr); if (debug) fprintf(stderr, "Connecting to %s ...\n", host); if (connect(sfd, (struct sockaddr *)&d_addr, sizeof(struct sockaddr)) == -1) err(EXIT_FAILURE, "connect()"); if (debug) fprintf(stderr, "Connected.\n"); if (optind == argc) { while ((p = fgets(buf, sizeof(buf), stdin)) != NULL) { buf[strlen(buf) - 1] = '\0'; ret |= grab_article(fp, buf, strip); } } else { for (; optind < argc; optind++) ret |= grab_article(fp, argv[optind], strip); } close(sfd); exit(ret); }