#include #include #include #include #include #include #include #include enum state { STATE_CONNECTED, STATE_GROUP_JOINED, STATE_ARTICLE_RECV, STATE_LAST }; int main(int argc, char **argv) { struct addrinfo hints = {0}; struct addrinfo *addr; int sockfd; FILE *sockf; char *host, *group, *messageid; if (argc == 2 && strchr(argv[1], '@')) { host = "news.gmane.org"; group = strdup("gmane.linux.kernel"); messageid = argv[1]; } else if (argc == 3 && strchr(argv[2], '@')) { host = "news.gmane.org"; group = argv[1]; if (strncmp(group, "gmane.", sizeof("gmane.") - 1)) { group = malloc(strlen(argv[1]) + sizeof("gmane.")); sprintf(group, "gmane.%s", argv[1]); } char *g = group; while ((g = strchr(g, '-'))) *g = '.'; messageid = argv[2]; } else if (argc == 4) { host = argv[1]; group = strdup(argv[2]); messageid = argv[3]; } else { fprintf(stderr, "Usage: %s host group message-id\n" "or: %s message-id\n", *argv, *argv); return 1; } hints.ai_protocol = IPPROTO_TCP; if (getaddrinfo(host, "119", &hints, &addr)) { perror("getaddrinfo"); return 1; } sockfd = socket(addr->ai_family, SOCK_STREAM, addr->ai_protocol); if (sockfd < 0) { perror("socket"); goto err_free_addr; } sockf = fdopen(sockfd, "r"); if (connect(sockfd, addr->ai_addr, addr->ai_addrlen)) { perror("connect"); goto err_close_sock; } enum state state = STATE_CONNECTED; while (state != STATE_LAST) { char buff[512]; int r = -1; if (fgets(buff, sizeof(buff), sockf) == NULL) { perror("read response"); goto err_close_sock; } switch (state) { case STATE_CONNECTED: if (strncmp(buff, "200 ", 4)) { fprintf(stderr, "Invalid response: %s\n", buff); goto err_close_sock; } r = snprintf(buff, sizeof(buff), "group %s\r\n", group); break; case STATE_GROUP_JOINED: if (strncmp(buff, "211 ", 4)) { fprintf(stderr, "Invalid response: %s\n", buff); goto err_close_sock; } if (messageid[0] == '<') { r = snprintf(buff, sizeof(buff), "article %s\r\n", messageid); } else { r = snprintf(buff, sizeof(buff), "article <%s>\r\n", messageid); } break; case STATE_ARTICLE_RECV: if (strncmp(buff, "220 ", 4)) { fprintf(stderr, "Invalid response: %s\n", buff); goto err_close_sock; } while (fgets(buff, sizeof(buff), sockf) != NULL) { if (!strcmp(buff, "..\r\n")) puts("."); else if (!strcmp(buff, ".\r\n")) break; else fputs(buff, stdout); } r = snprintf(buff, sizeof(buff), "quit\r\n"); break; case STATE_LAST: break; } state++; if (r > 0) write(sockfd, buff, r); } close(sockfd); freeaddrinfo(addr); free(group); return 0; err_close_sock: close(sockfd); err_free_addr: freeaddrinfo(addr); free(group); return 1; }