📄 innxmit.c
字号:
/* $Revision: 1.14 $**** Transmit articles to remote site.*/#include "configdata.h"#include <stdio.h>#include <ctype.h>#include <errno.h>#include <signal.h>#include <setjmp.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/stat.h>#include <fcntl.h>#if defined(DO_NEED_TIME)#include <time.h>#endif /* defined(DO_NEED_TIME) */#include <sys/time.h>#include <sys/uio.h>#include "nntp.h"#include "paths.h"#include "logging.h"#include "libinn.h"#include "clibrary.h"#include "qio.h"#include "dbz.h"#include "macros.h"/*** Supported encoding schemes.*/typedef enum _MIMEXFERTYPE { MTnotmime, MTquotedprintable, MTbase64} MIMEXFERTYPE;#define OUTPUT_BUFFER_SIZE (16 * 1024)/*** Syslog formats - collected together so they remain consistent*/STATIC char STAT1[] = "%s stats offered %lu accepted %lu refused %lu rejected %lu";STATIC char STAT2[] = "%s times user %.3f system %.3f elapsed %.3f";STATIC char GOT_RESENDIT[] = "%s requeued %s %s";STATIC char CANT_CONNECT[] = "%s connect failed %s";STATIC char CANT_AUTHENTICATE[] = "%s authenticate failed %s";STATIC char IHAVE_FAIL[] = "%s ihave failed %s";/*** Global variables.*/STATIC BOOL AlwaysRewrite;STATIC BOOL Debug;STATIC BOOL DoRequeue = TRUE;STATIC BOOL Purging;STATIC BOOL Slavish;STATIC BOOL STATprint;STATIC BOOL Mime;STATIC MIMEXFERTYPE MimeArticle = MTnotmime;STATIC char *BATCHname;STATIC char *BATCHtemp;STATIC char *REMhost;STATIC double STATbegin;STATIC double STATend;STATIC FILE *BATCHfp;STATIC int FromServer;STATIC int ToServer;STATIC QIOSTATE *BATCHqp;STATIC SIGVAR GotAlarm;STATIC SIGVAR GotInterrupt;STATIC SIGVAR JMPyes;STATIC jmp_buf JMPwhere;STATIC char *REMbuffer;STATIC char *REMbuffptr;STATIC char *REMbuffend;STATIC unsigned long STATaccepted;STATIC unsigned long STAToffered;STATIC unsigned long STATrefused;STATIC unsigned long STATrejected;/*** Find the history file entry for the Message-ID and return a file** positioned at the third field.*/STATIC FILE *HistorySeek(MessageID) char *MessageID;{ static char History[] = _PATH_HISTORY; static FILE *F; register char *p; register char *q; register int i; datum key; datum val; OFFSET_T offset; /* Open the history file. */ if (F == NULL) { if (dbminit(History) < 0) { (void)fprintf(stderr, "Can't set up \"%s\" database, %s\n", History, strerror(errno)); exit(1); } if ((F = fopen(History, "r")) == NULL) { (void)fprintf(stderr, "Can't open \"%s\" for reading, %s\n", History, strerror(errno)); exit(1); } } /* Do the lookup. */ key.dsize = strlen(MessageID) + 1; key.dptr = MessageID; val = dbzfetch(key); if (val.dptr == NULL || val.dsize != sizeof offset) return NULL; /* Get the seek offset, and seek. */ for (p = val.dptr, q = (char *)&offset, i = sizeof offset; --i >= 0; ) *q++ = *p++; if (fseek(F, offset, SEEK_SET) == -1) return NULL; return F;}/*** Return TRUE if the history file has the article expired.*/STATIC BOOLExpired(MessageID) char *MessageID;{ register int c; register int i; register FILE *F; if ((F = HistorySeek(MessageID)) == NULL) /* Assume the worst. */ return TRUE; /* Move to the filename fields. */ for (i = 2; (c = getc(F)) != EOF && c != '\n'; ) if (c == HIS_FIELDSEP && --i == 0) break; if (c != HIS_FIELDSEP) return TRUE; /* See if we get any filename before the end of the line. */ while ((c = getc(F)) != EOF && c != '\n') if (!ISWHITE(c)) /* Found non-whitespace; assume it's a filename. */ return FALSE; return TRUE;}/*** Flush and reset the site's output buffer. Return FALSE on error.*/STATIC BOOLREMflush(){ int i; i = xwrite(ToServer, REMbuffer, (int)(REMbuffptr - REMbuffer)); REMbuffptr = REMbuffer; return i < 0 ? FALSE : TRUE;}/*** Send a line to the server, adding the dot escape and \r\n.*/STATIC BOOLREMwrite(p, i) register char *p; register int i;{ static char HDR[] = "Content-Transfer-Encoding:"; static char COD[] = "Content-Transfer-Encoding: quoted-printable\r\n"; register char *dest; int size; /* Buffer too full? */ if (REMbuffend - REMbuffptr < i + 3) { if (!REMflush()) return FALSE; if (REMbuffend - REMbuffer < i + 3) { /* Line too long -- grow buffer. */ size = i * 2; RENEW(REMbuffer, char, size); REMbuffend = &REMbuffer[size]; } } if (MimeArticle != MTnotmime) if ((*p == 'C' && EQn(p, HDR, STRLEN(HDR))) || ((*p == 'C' || *p == 'c') && caseEQn(p, HDR, STRLEN(HDR)))) { (void)memcpy((POINTER)REMbuffptr, (POINTER)COD, STRLEN(COD)); REMbuffptr += STRLEN(COD); return TRUE; } /* Dot escape, text of the line, line terminator. */ if (*p == '.') *REMbuffptr++ = '.'; if (i > MEMCPY_THRESHOLD) { (void)memcpy((POINTER)REMbuffptr, (POINTER)p, (SIZE_T)i); REMbuffptr += i; } else { for (dest = REMbuffptr, i++; --i > 0; ) *dest++ = *p++; REMbuffptr = dest; } *REMbuffptr++ = '\r'; *REMbuffptr++ = '\n'; return TRUE;}/*** Send a line to the server, adding the dot escape and \r\n.*/STATIC BOOLREMwriteQuoted(p, i) register char *p; register int i;{ static char HEXDIGITS[] = "0123456789ABCDEF"; register char *dest; register int size; register int count; register int prev; /* Buffer too full? */ if (REMbuffend - REMbuffptr < i + 3) { if (!REMflush()) return FALSE; if (REMbuffend - REMbuffer < i + 3) { /* Line too long -- grow buffer. */ size = i * 2; RENEW(REMbuffer, char, size); REMbuffend = &REMbuffer[size]; } } for (count = 0, prev = 255, dest = REMbuffptr, i++; --i > 0; ) { if ((*p < 32 && *p != '\t') || *p == '=' || *p >= 127 || (count == 0 && *p =='.')) { *dest++ = '='; *dest++ = HEXDIGITS[*p >> 4]; *dest++ = HEXDIGITS[*p & 0x0F]; p++; count += 3; prev = 'A'; } else { prev = *dest++ = *p++; count++; } if (count > 72) { *dest++ = '='; *dest++ = '\r'; *dest++ = '\n'; count = 0; prev = '\n'; } } if (prev == ' ' || prev == '\t') *dest++ = '='; REMbuffptr = dest; *REMbuffptr++ = '\r'; *REMbuffptr++ = '\n'; return TRUE;}/*** Print transfer statistics, clean up, and exit.*/STATIC NORETURNExitWithStats(x) int x;{ static char QUIT[] = "quit"; TIMEINFO Now; double usertime; double systime; if (!Purging) { (void)REMwrite(QUIT, STRLEN(QUIT)); (void)REMflush(); } (void)GetTimeInfo(&Now); STATend = TIMEINFOasDOUBLE(Now); if (GetResourceUsage(&usertime, &systime) < 0) { usertime = 0; systime = 0; } if (STATprint) { (void)printf(STAT1, REMhost, STAToffered, STATaccepted, STATrefused, STATrejected); (void)printf("\n"); (void)printf(STAT2, REMhost, usertime, systime, STATend - STATbegin); (void)printf("\n"); } syslog(L_NOTICE, STAT1, REMhost, STAToffered, STATaccepted, STATrefused, STATrejected); syslog(L_NOTICE, STAT2, REMhost, usertime, systime, STATend - STATbegin); if (BATCHfp != NULL && unlink(BATCHtemp) < 0 && errno != ENOENT) (void)fprintf(stderr, "Can't remove \"%s\", %s\n", BATCHtemp, strerror(errno)); exit(x); /* NOTREACHED */}/*** Close the batchfile and the temporary file, and rename the temporary** to be the batchfile.*/STATIC voidCloseAndRename(){ /* Close the files, rename the temporary. */ QIOclose(BATCHqp); if (ferror(BATCHfp) || fflush(BATCHfp) == EOF || fclose(BATCHfp) == EOF) { (void)unlink(BATCHtemp); (void)fprintf(stderr, "Can't close \"%s\", %s\n", BATCHtemp, strerror(errno)); ExitWithStats(1); } if (rename(BATCHtemp, BATCHname) < 0) { (void)fprintf(stderr, "Can't rename \"%s\", %s\n", BATCHtemp, strerror(errno)); ExitWithStats(1); }}/*** Requeue an article, opening the temp file if we have to. If we get** a file write error, exit so that the original input is left alone.*/STATIC voidRequeue(Article, MessageID) char *Article; char *MessageID;{ /* Temp file already open? */ if (BATCHfp == NULL) { (void)mktemp(BATCHtemp); if ((BATCHfp = fopen(BATCHtemp, "w")) == NULL) { (void)fprintf(stderr, "Can't open \"%s\", %s\n", BATCHtemp, strerror(errno)); ExitWithStats(1); } } /* Called only to get the file open? */ if (Article == NULL) return; if (MessageID != NULL) (void)fprintf(BATCHfp, "%s %s\n", Article, MessageID); else (void)fprintf(BATCHfp, "%s\n", Article); if (fflush(BATCHfp) == EOF || ferror(BATCHfp)) { (void)fprintf(stderr, "Can't requeue \"%s\", %s\n", Article, strerror(errno)); ExitWithStats(1); }}/*** Requeue an article then copy the rest of the batch file out.*/STATIC voidRequeueRestAndExit(Article, MessageID) char *Article; char *MessageID;{ register char *p; if (!AlwaysRewrite && STATaccepted == 0 && STATrejected == 0 && STATrefused == 0) { (void)fprintf(stderr, "Nothing sent -- leaving batchfile alone.\n"); ExitWithStats(1); } (void)fprintf(stderr, "Rewriting batch file and exiting.\n"); Requeue(Article, MessageID); for ( ; ; ) { if ((p = QIOread(BATCHqp)) == NULL) { if (QIOerror(BATCHqp)) { (void)fprintf(stderr, "Can't read \"%s\", %s\n", BATCHname, strerror(errno)); ExitWithStats(1); } if (QIOtoolong(BATCHqp)) { (void)fprintf(stderr, "Skipping long line in \"%s\".\n", BATCHname); (void)QIOread(BATCHqp); continue; } /* Normal EOF. */ break; } if (fprintf(BATCHfp, "%s\n", p) == EOF || ferror(BATCHfp)) { (void)fprintf(stderr, "Can't requeue \"%s\", %s\n", p, strerror(errno)); ExitWithStats(1); } } CloseAndRename(); ExitWithStats(1);}/*** Clean up the NNTP escapes from a line.*/STATIC char *REMclean(buff) char *buff;{ char *p; if ((p = strchr(buff, '\r')) != NULL) *p = '\0'; if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; /* The dot-escape is only in text, not command responses. */ return buff;}/*** Read a line of input, with timeout. Also handle \r\n-->\n mapping** and the dot escape. Return TRUE if okay, *or we got interrupted.**/STATIC BOOLREMread(start, size) char *start; int size;{ static int count; static char buffer[BUFSIZ]; static char *bp; register char *p; register char *q; register char *end; struct timeval t; FDSET rmask; int i; char c; if (!REMflush()) return FALSE; for (p = start, end = &start[size - 1]; ; ) { if (count == 0) { /* Fill the buffer. */ Again: FD_ZERO(&rmask); FD_SET(FromServer, &rmask); t.tv_sec = 10 * 60; t.tv_usec = 0; i = select(FromServer + 1, &rmask, (FDSET *)NULL, (FDSET *)NULL, &t); if (GotInterrupt) return TRUE; if (i < 0) { if (errno == EINTR) goto Again; return FALSE; } if (i == 0 || !FD_ISSET(FromServer, &rmask)) return FALSE; count = read(FromServer, buffer, sizeof buffer); if (GotInterrupt) return TRUE; if (count <= 0) return FALSE; bp = buffer; } /* Process next character. */ count--; c = *bp++; if (c == '\n') break; if (p < end) *p++ = c; } /* We know we got \n; if previous char was \r, turn it into \n. */ if (p > start && p < end && p[-1] == '\r') p[-1] = '\n'; *p = '\0'; /* Handle the dot escape. */ if (*p == '.') { if (p[1] == '\n' && p[2] == '\0') /* EOF. */ return FALSE; for (q = &start[1]; (*p++ = *q++) != '\0'; ) continue; } return TRUE;}/*** Handle the interrupt.*/static voidInterrupted(Article, MessageID) char *Article; char *MessageID;{ (void)fprintf(stderr, "Interrupted\n"); RequeueRestAndExit(Article, MessageID);}/*** Send a whole article to the server.*/STATIC BOOLREMsendarticle(Article, MessageID, qp) char *Article; char *MessageID; register QIOSTATE *qp;{ static char TERM[] = ".\r\n"; register char *p; register BOOL ok; register BOOL InHeaders; char buff[NNTP_STRLEN]; for (InHeaders = TRUE; ; ) { if ((p = QIOread(qp)) == NULL) { if (QIOerror(qp)) { (void)fprintf(stderr, "Can't read \"%s\", %s\n", Article, strerror(errno)); return FALSE; } if (QIOtoolong(qp)) { (void)fprintf(stderr, "Line too long in \"%s\"\n", Article); (void)QIOread(BATCHqp); continue; } /* Normal EOF. */ break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -