1 /* $Id: nntpget.c 6135 2003-01-19 01:15:40Z rra $
3 ** Connect to a remote site, and get news from it to offer to our local
4 ** server. Read list on stdin, or get it via NEWNEWS command. Writes
5 ** list of articles still needed to stdout.
10 #include "portable/socket.h"
11 #include "portable/time.h"
16 /* Needed on AIX 4.1 to get fd_set and friends. */
17 #ifdef HAVE_SYS_SELECT_H
18 # include <sys/select.h>
21 #include "inn/history.h"
22 #include "inn/innconf.h"
23 #include "inn/messages.h"
29 ** All information about a site we are connected to.
31 typedef struct _SITE {
44 static struct iovec SITEvec[2];
45 static char SITEv1[] = "\r\n";
46 static char READER[] = "mode reader";
47 static unsigned long STATgot;
48 static unsigned long STAToffered;
49 static unsigned long STATsent;
50 static unsigned long STATrejected;
51 static struct history *History;
56 ** Read a line of input, with timeout.
59 SITEread(SITE *sp, char *start)
68 for (p = start, end = &start[NNTP_STRLEN - 1]; ; ) {
70 /* Fill the buffer. */
73 FD_SET(sp->Rfd, &rmask);
74 t.tv_sec = DEFAULT_TIMEOUT;
76 i = select(sp->Rfd + 1, &rmask, NULL, NULL, &t);
83 || !FD_ISSET(sp->Rfd, &rmask)
84 || (sp->Count = read(sp->Rfd, sp->Buffer, sizeof sp->Buffer)) < 0)
91 /* Process next character. */
100 /* If last two characters are \r\n, kill the \r as well as the \n. */
101 if (p > start && p < end && p[-1] == '\r')
109 ** Send a line to the server, adding \r\n. Don't need to do dot-escape
110 ** since it's only for sending DATA to local site, and the data we got from
111 ** the remote site already is escaped.
114 SITEwrite(SITE *sp, const char *p, int i)
116 SITEvec[0].iov_base = (char *) p;
117 SITEvec[0].iov_len = i;
118 return xwritev(sp->Wfd, SITEvec, 2) >= 0;
123 SITEconnect(char *host)
130 /* Connect and identify ourselves. */
132 i = NNTPconnect(host, NNTP_PORT, &From, &To, (char *)NULL);
134 host = innconf->server;
136 die("no server specified and server not set in inn.conf");
137 i = NNTPlocalopen(&From, &To, (char *)NULL);
140 sysdie("cannot connect to %s", host);
142 if (NNTPsendpassword(host, From, To) < 0)
143 sysdie("cannot authenticate to %s", host);
145 /* Build the structure. */
146 sp = xmalloc(sizeof(SITE));
148 sp->Rfd = fileno(From);
149 sp->Wfd = fileno(To);
157 ** Send "quit" to a site, and get its reply.
162 char buff[NNTP_STRLEN];
164 SITEwrite(sp, "quit", 4);
170 HIShaveit(char *mesgid)
172 return HIScheck(History, mesgid);
180 fprintf(stderr, "Usage: nntpget"
181 " [ -d dist -n grps [-f file | -t time -u file]] host\n");
187 main(int ac, char *av[])
189 char buff[NNTP_STRLEN];
190 char mesgid[NNTP_STRLEN];
192 char *msgidfile = NULL;
206 bool Verbose = false;
210 /* First thing, set up our identity. */
211 message_program_name = "nntpget";
214 distributions = NULL;
219 if (!innconf_read(NULL))
225 while ((i = getopt(ac, av, "d:f:n:t:ovu:")) != EOF)
231 distributions = optarg;
238 Usage("only one of -f, -t, or -u may be given");
239 if (stat(optarg, &Sb) < 0)
240 sysdie("cannot stat %s", optarg);
241 gt = gmtime(&Sb.st_mtime);
242 /* Y2K: NNTP Spec currently allows only two digit years. */
243 snprintf(tbuff, sizeof(tbuff), "%02d%02d%02d %02d%02d%02d GMT",
244 gt->tm_year % 100, gt->tm_mon + 1, gt->tm_mday,
245 gt->tm_hour, gt->tm_min, gt->tm_sec);
252 /* Open the history file. */
253 path = concatpath(innconf->pathdb, _PATH_HISTORY);
254 History = HISopen(path, innconf->hismethod, HIS_RDONLY);
256 sysdie("cannot open history");
262 Usage("only one of -t or -f may be given");
272 Usage("no host given");
274 /* Set up the scatter/gather vectors used by SITEwrite. */
275 SITEvec[1].iov_base = SITEv1;
276 SITEvec[1].iov_len = strlen(SITEv1);
278 /* Connect to the remote server. */
279 if ((Remote = SITEconnect(av[0])) == NULL)
280 sysdie("cannot connect to %s", av[0]);
281 if (!SITEwrite(Remote, READER, (int)strlen(READER))
282 || !SITEread(Remote, buff))
283 sysdie("cannot start reading");
287 if (distributions || Groups)
288 Usage("no -d or -n flags allowed when reading stdin");
291 /* Ask the server for a list of what's new. */
295 snprintf(buff, sizeof(buff), "NEWNEWS %s %s <%s>",
296 Groups, Since, distributions);
298 snprintf(buff, sizeof(buff), "NEWNEWS %s %s", Groups, Since);
299 if (!SITEwrite(Remote, buff, (int)strlen(buff))
300 || !SITEread(Remote, buff))
301 sysdie("cannot start list");
302 if (buff[0] != NNTP_CLASS_OK) {
304 die("protocol error from %s, got %s", Remote->Name, buff);
307 /* Create a temporary file. */
308 msgidfile = concatpath(innconf->pathtmp, "nntpgetXXXXXX");
309 msgidfd = mkstemp(msgidfile);
311 sysdie("cannot create a temporary file");
312 F = fopen(msgidfile, "w+");
314 sysdie("cannot open %s", msgidfile);
316 /* Read and store the Message-ID list. */
318 if (!SITEread(Remote, buff)) {
319 syswarn("cannot read from %s", Remote->Name);
324 if (strcmp(buff, ".") == 0)
326 if (Offer && HIShaveit(buff))
328 if (fprintf(F, "%s\n", buff) == EOF || ferror(F)) {
329 syswarn("cannot write %s", msgidfile);
335 if (fflush(F) == EOF) {
336 syswarn("cannot flush %s", msgidfile);
341 fseeko(F, 0, SEEK_SET);
345 /* Connect to the local server. */
346 if ((Local = SITEconnect((char *)NULL)) == NULL) {
347 syswarn("cannot connect to local server");
353 /* Loop through the list of Message-ID's. */
354 while (fgets(mesgid, sizeof mesgid, F) != NULL) {
356 if ((p = strchr(mesgid, '\n')) != NULL)
360 /* See if the local server wants it. */
362 snprintf(buff, sizeof(buff), "ihave %s", mesgid);
363 if (!SITEwrite(Local, buff, (int)strlen(buff))
364 || !SITEread(Local, buff)) {
365 syswarn("cannot offer %s", mesgid);
368 if (atoi(buff) != NNTP_SENDIT_VAL)
372 /* Try to get the article. */
373 snprintf(buff, sizeof(buff), "article %s", mesgid);
374 if (!SITEwrite(Remote, buff, (int)strlen(buff))
375 || !SITEread(Remote, buff)) {
376 syswarn("cannot get %s", mesgid);
377 printf("%s\n", mesgid);
380 if (atoi(buff) != NNTP_ARTICLE_FOLLOWS_VAL) {
382 SITEwrite(Local, ".", 1);
383 if (!SITEread(Local, buff)) {
384 syswarn("no reply after %s", mesgid);
392 notice("%s...", mesgid);
394 /* Read each line in the article and write it. */
395 for (Error = false; ; ) {
396 if (!SITEread(Remote, buff)) {
397 syswarn("cannot read %s from %s", mesgid, Remote->Name);
402 if (!SITEwrite(Local, buff, (int)strlen(buff))) {
403 syswarn("cannot send %s", mesgid);
409 printf("%s\n", buff);
410 if (strcmp(buff, ".") == 0)
414 printf("%s\n", mesgid);
419 /* How did the local server respond? */
421 if (!SITEread(Local, buff)) {
422 syswarn("no reply after %s", mesgid);
423 printf("%s\n", mesgid);
427 if (i == NNTP_TOOKIT_VAL)
429 if (i == NNTP_RESENDIT_VAL) {
430 printf("%s\n", mesgid);
433 syswarn("%s to %s", buff, mesgid);
438 /* Write rest of the list, close the input. */
440 while (fgets(mesgid, sizeof mesgid, F) != NULL) {
441 if ((p = strchr(mesgid, '\n')) != NULL)
443 printf("%s\n", mesgid);
448 /* Remove our temp file. */
449 if (msgidfile && unlink(msgidfile) < 0)
450 syswarn("cannot remove %s", msgidfile);
457 /* Update timestamp file? */
459 if ((F = fopen(Update, "w")) == NULL)
460 sysdie("cannot update %s", Update);
461 fprintf(F, "got %ld offered %ld sent %ld rejected %ld\n",
462 STATgot, STAToffered, STATsent, STATrejected);
463 if (ferror(F) || fclose(F) == EOF)
464 sysdie("cannot update %s", Update);