1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netdb.h>
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;
}
|